akka-typed(1) - actor生命周期管理
akka-typed的actor从创建、启用、状态转换、停用、监视等生命周期管理方式和akka-classic还是有一定的不同之处。这篇我们就介绍一下akka-typed的actor生命周期管理。
每一种actor都是通过定义它的行为属性behavior形成模版,然后由对上一层的父辈actor用spawn方法产生actor实例的。产生的actor实例加入一个系统的由上至下树形结构,直接在spawn产生自己的父辈之下。akka-typed的守护guardian-actor,即根部root-actor是通过在定义ActorSystem时指定并产生的。如下:
val config = ConfigFactory.load("application.conf")
val man: ActorSystem[GreetStarter.Command] = ActorSystem(GreetStarter(), "greetDemo",config)
man ! GreetStarter.RepeatedGreeting("Tiger",.seconds)
在某种意义上,这个ActorSystem实例man就代表root-actor。我们可以向man发送消息然后由GreetStarter的behavior用自己的ActorContext进行spawn,stop,watch及分派计算任务等,其实就是一个程序的集线器:
object GreetStarter {
import Messages._
def apply(): Behavior[SayHi] = {
Behaviors.setup { ctx =>
val props = DispatcherSelector.fromConfig("akka.actor.default-blocking-io-dispatcher")
val helloActor = ctx.spawn(HelloActor(), "hello-actor",props)
val greeter = ctx.spawn(Greeter(helloActor), "greeter")
ctx.watch(greeter)
ctx.watchWith(helloActor,StopWorker("something happend"))
Behaviors.receiveMessage { who =>
if (who.name == "stop") {
ctx.stop(helloActor)
ctx.stop(greeter)
Behaviors.stopped
} else {
greeter ! who
Behaviors.same
}
}
}
}
}
但是,总有时候我们需要在root-actor的ActorContext之外来进行一些制造、使用actor的操作。下面这个官方文档上的例子是很好的示范:
import akka.actor.typed.Behavior
import akka.actor.typed.SpawnProtocol
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl.LoggerOps object HelloWorldMain {
def apply(): Behavior[SpawnProtocol.Command] =
Behaviors.setup { context =>
// Start initial tasks
// context.spawn(...) SpawnProtocol()
}
} object Main extends App {
implicit val system: ActorSystem[SpawnProtocol.Command] =
ActorSystem(HelloWorldMain(), "hello") // needed in implicit scope for ask (?)
import akka.actor.typed.scaladsl.AskPattern._
implicit val ec: ExecutionContext = system.executionContext
implicit val timeout: Timeout = Timeout(.seconds) val greeter: Future[ActorRef[HelloWorld.Greet]] =
system.ask(SpawnProtocol.Spawn(behavior = HelloWorld(), name = "greeter", props = Props.empty, _)) val greetedBehavior = Behaviors.receive[HelloWorld.Greeted] { (context, message) =>
context.log.info2("Greeting for {} from {}", message.whom, message.from)
Behaviors.stopped
} val greetedReplyTo: Future[ActorRef[HelloWorld.Greeted]] =
system.ask(SpawnProtocol.Spawn(greetedBehavior, name = "", props = Props.empty, _)) for (greeterRef <- greeter; replyToRef <- greetedReplyTo) {
greeterRef ! HelloWorld.Greet("Akka", replyToRef)
}
...
}
可以看到所有操作都在actor框架之外进行的。这个SpawnProtocol本身就是一个actor,如下:
object SpawnProtocol {
...
final case class Spawn[T](behavior: Behavior[T], name: String, props: Props, replyTo: ActorRef[ActorRef[T]])
extends Command
...
def apply(): Behavior[Command] =
Behaviors.receive { (ctx, msg) =>
msg match {
case Spawn(bhvr, name, props, replyTo) =>
val ref =
if (name == null || name.equals(""))
ctx.spawnAnonymous(bhvr, props)
else {
@tailrec def spawnWithUniqueName(c: Int): ActorRef[Any] = {
val nameSuggestion = if (c == ) name else s"$name-$c"
ctx.child(nameSuggestion) match {
case Some(_) => spawnWithUniqueName(c + ) // already taken, try next
case None => ctx.spawn(bhvr, nameSuggestion, props)
}
}
spawnWithUniqueName()
}
replyTo ! ref
Behaviors.same
}
}
}
外界通过发送Spawn消息来指定产生新的actor。
actor的状态切换就是从一种behavior转到另一种behavior。我们可以自定义behavior或者用现成的Behaviors.???。如果只是涉及内部变量变化,那么可以直接生成带着变量的当前behavior,如下:
object HelloWorldBot {
def apply(max: Int): Behavior[HelloWorld.Greeted] = {
bot(, max)
}
private def bot(greetingCounter: Int, max: Int): Behavior[HelloWorld.Greeted] =
Behaviors.receive { (context, message) =>
val n = greetingCounter +
context.log.info2("Greeting {} for {}", n, message.whom)
if (n == max) {
Behaviors.stopped
} else {
message.from ! HelloWorld.Greet(message.whom, context.self)
bot(n, max)
}
}
}
actor停用可以由直属父辈actor的ActorContext.stop或者自身的Behaviors.stopped来实现。Behaviors.stopped可以带入一个清理函数。在actor完全停止之前进行一些清理操作:
object MasterControlProgram {
sealed trait Command
final case class SpawnJob(name: String) extends Command
case object GracefulShutdown extends Command
// Predefined cleanup operation
def cleanup(log: Logger): Unit = log.info("Cleaning up!")
def apply(): Behavior[Command] = {
Behaviors
.receive[Command] { (context, message) =>
message match {
case SpawnJob(jobName) =>
context.log.info("Spawning job {}!", jobName)
context.spawn(Job(jobName), name = jobName)
Behaviors.same
case GracefulShutdown =>
context.log.info("Initiating graceful shutdown...")
// perform graceful stop, executing cleanup before final system termination
// behavior executing cleanup is passed as a parameter to Actor.stopped
Behaviors.stopped { () =>
cleanup(context.system.log)
}
}
}
.receiveSignal {
case (context, PostStop) =>
context.log.info("Master Control Program stopped")
Behaviors.same
}
}
}
实际上一个actor转入停用stop状态可以在另一个作为监视actor的receiveSignal获取,如下:
object GreetStarter {
import Messages._
def apply(): Behavior[SayHi] = {
Behaviors.setup { ctx =>
val props = DispatcherSelector.fromConfig("akka.actor.default-blocking-io-dispatcher")
val helloActor = ctx.spawn(HelloActor(), "hello-actor",props)
val greeter = ctx.spawn(Greeter(helloActor), "greeter")
ctx.watch(greeter)
ctx.watchWith(helloActor,StopWorker("something happend"))
Behaviors.receiveMessage { who =>
if (who.name == "stop") {
ctx.stop(helloActor)
ctx.stop(greeter)
Behaviors.stopped
} else {
greeter ! who
Behaviors.same
}
}.receiveSignal {
case (context, Terminated(ref)) =>
context.log.info("{} stopped!", ref.path.name)
Behaviors.same
}
}
}
}
下面是.receiveSignal函数及其捕获的Signal消息:
trait Receive[T] extends Behavior[T] {
def receiveSignal(onSignal: PartialFunction[(ActorContext[T], Signal), Behavior[T]]): Behavior[T]
}
trait Signal
/**
* Lifecycle signal that is fired upon restart of the Actor before replacing
* the behavior with the fresh one (i.e. this signal is received within the
* behavior that failed).
*/
sealed abstract class PreRestart extends Signal
case object PreRestart extends PreRestart {
def instance: PreRestart = this
}
/**
* Lifecycle signal that is fired after this actor and all its child actors
* (transitively) have terminated. The [[Terminated]] signal is only sent to
* registered watchers after this signal has been processed.
*/
sealed abstract class PostStop extends Signal
// comment copied onto object for better hints in IDEs
/**
* Lifecycle signal that is fired after this actor and all its child actors
* (transitively) have terminated. The [[Terminated]] signal is only sent to
* registered watchers after this signal has been processed.
*/
case object PostStop extends PostStop {
def instance: PostStop = this
}
object Terminated {
def apply(ref: ActorRef[Nothing]): Terminated = new Terminated(ref)
def unapply(t: Terminated): Option[ActorRef[Nothing]] = Some(t.ref)
}
akka-typed(1) - actor生命周期管理的更多相关文章
- Akka(2):Actor生命周期管理 - 监控和监视
在开始讨论Akka中对Actor的生命周期管理前,我们先探讨一下所谓的Actor编程模式.对比起我们习惯的行令式(imperative)编程模式,Actor编程模式更接近现实中的应用场景和功能测试模式 ...
- [翻译]AKKA笔记 - ACTOR生命周期 - 基本 -5
原文地址:http://rerun.me/2014/10/21/akka-notes-actor-lifecycle-basic/ (请注意这了讨论的生命周期并不包括 preRestart 或者pos ...
- Akka之Actor生命周期
我们首先来看一下官方给出的Actor的声明周期的图: 在上图中,Actor系统中的路径代表一个地方,其可能会被活着的Actor占据.最初路径都是空的.在调用actorOf()时,将会为指定的路径分配根 ...
- ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理
ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...
- 【转】Tomcat组件生命周期管理
Tomcat组件生命周期管理 Tomcat中Server,Service,Connector,Engine,Host,Context,它们都实现了org.apache.catalina.Lifecyc ...
- Castle IOC容器组件生命周期管理
主要内容 1.生命处理方式 2.自定义生命处理方式 3.生命周期处理 一.生命处理方式 我们通常创建一个组件的实例使用new关键字,这样每次创建出来的都是一个新的实例,如果想要组件只有一个实例,我们会 ...
- Java实现生命周期管理机制
先扯再说 最近一直在研究某个国产开源的MySQL数据库中间件,拉下其最新版的代码到eclipse后,启动起来,然后做各种测试和代码追踪:用完想要关闭它时,拉出它的STOP类想要运行时,发现这个类里赫然 ...
- 6、Khala的登录生命周期管理
khala能够对设备进行生命周期管理,并提供了与生命周期相关的接口,用户只需在具体的设备类型实现类中重写这些生命周期接口,即可享受khala对于生命周期管理的同时定制与业务相关的操作.具体接口解释如下 ...
- 依赖注入及AOP简述(十一)——生命周期管理 .
2. 生命周期管理 各种依赖注入框架提供了替开发者管理各种Scope的便利功能,随之而来的就必然是被管理的依赖对象的生命周期管理的问题.所谓生命周期管理,就是一个对象在它所属的Scope中从被 ...
随机推荐
- javascript中Function、ArrowFunction和GeneratorFunction介绍
ECMAScript规范中对Function的文档描述,我认为是ECMAScript规范中最复杂也是最不好理解的一部分,它涉及到了各方面.光对Function就分了Function Definitio ...
- Effective C++学习记录
Effective C++算是看完了,但是并没有完全理解,也做不到记住所有,在此记录下55个条款及条款末的"请记住". 让自己习惯C++ 条款01:视C++为一个语言联邦 ① C ...
- [UWP]推荐一款很Fluent Design的bilibili UWP客户端 : 哔哩
UWP已经有好几个Bilibili的客户端,最近有多了一个: 哔哩 - Microsoft Store 作者云之幻是一位很擅长设计的UWP开发者,我也从他那里学到了很多设计方面的技巧.它还是一位Bil ...
- Android EXCEL 解析 xls 和 xlsx,方法其实很简单
前言 Excel 解析,一般来说是在服务端进行的,但是如果移动端要实现解析Excel的功能,那也是有实现的方法的. 不过由于Android 原生用Java/Kotlin实现,所以也可以参考服务端解析E ...
- 面试官:小伙子,听说你看过ThreadLocal源码?(万字图文深度解析ThreadLocal)
前言 Ym8V9H.png (高清无损原图.pdf关注公众号后回复 ThreadLocal 获取,文末有公众号链接) 前几天写了一篇AQS相关的文章:我画了35张图就是为了让你深入 AQS,反响不错, ...
- 【FPGA篇章七】FPGA系统任务:详述常用的一些系统函数以及使用方法
欢迎大家关注我的微信公众账号,支持程序媛写出更多优秀的文章 系统任务和系统函数是Verilog标准的一部分,都以字符"$"为开头.系统任务可划分为六类,下面分别给出一些常用任务的用 ...
- ReentrantLock源码解析
ReentrantLock 1 数据结构 从上图可以看出,ReentrantLock的功能都是通过sync这个对象提供的. public class ReentrantLock implements ...
- Netty入门一:何为Netty
先了解java的网络编程 Netty为何支持高并发 netty是基于java的nio非阻塞通信,而原始的阻塞通信无法满足高并发.下面我们通过两幅图来简要说明 BIO: 这种模式下一个线程处理一个连接, ...
- xsser和XSStrike
0x01 xsser xsser是一款用于针对Web应用程序自动化挖掘.利用.报告xss漏洞的框架.kali默认安装. 命令行启动:xsser 图形化界面启动:xsser --gtk 帮助信息:xss ...
- layui select下拉菜单联动
做的比较简单,先从后台直接把第一级菜单输出,然后点击二级菜单的时候再动态展示 <div class="layui-inline"> <label class=&q ...