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中从被 ...
 
随机推荐
- 个人博客开发系列:Vue.js + Koa.js项目中使用JWT认证
			
前言 JWT(JSON Web Token),是为了在网络环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519). 更多的介绍和说明,以及各种原理,我在此就不多赘诉了.JWT不是一个新鲜 ...
 - JAVA_WEB--jsp语法
			
JSP声明 一个声明语句可以声明一个或多个变量.方法,供后面的Java代码使用.在JSP文件中,必须先声明这些变量和方法然后才能使用它们. JSP声明的语法格式: <%! declaration ...
 - XML--XML作用
			
XML 把数据从 HTML 分离 如果你需要在 HTML 文档中显示动态数据,那么每当数据改变时将花费大量的时间来编辑 HTML. 通过 XML,数据能够存储在独立的 XML 文件中.这样你就可以专注 ...
 - Hello World的五十种不同实现方法!!!!!
			
我们作为一名程序员,职业生涯中至少完成了一个“Hello, World!“程序.当我们学习一门新的语言时,“Hello, World!“通常是我们所写的第一个程序.程序员一般也都会使用多门语言,甚至有 ...
 - Java——Java面向对象
			
该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 概述: Java是面 ...
 - LTE基站开局流程
			
1.全局参数配置 MOD ENODEB :修改基站 ADD CNOPERATOR: 添加运营商 ADD CNOPERATORTA:添加跟踪区(TA) 2.设备参数配置(机柜.机框.RRU.光纤链 ...
 - Vular开发手记#1:设计并实现一个拼插式应用程序框架
			
可视化编(rxeditor)辑告一段落,在知乎上发了一个问题,询问前景,虽然看好的不多,但是关注度还是有的,目前为止积累了21w流量,因为这个事,开心了好长一段时间.这一个月的时间,主要在设计制作Vu ...
 - 浅谈mybatis如何半自动化解耦和ORM实现
			
在JAVA发展过程中,涌现出一系列的ORM框架,JPA,Hibernate,Mybatis和Spring jdbc,本系列,将来研究Mybatis. 通过研究mybatis源码,可将mybatis的大 ...
 - .net core 3.1 在iis上的发布(踩坑)
			
写这篇文章的目的是希望像我一样喜欢.net 的人在发布 core到 iis上时少走点弯路 网上找了些资料,其实实际操作比较简单,就是有几个坑很恶心 首先是你的服务器需要有core 的运行环境,安装前先 ...
 - Mockito不能mock final类的解决办法
			
Mockito是很常用的测试工具,使用过程中可能会遇到下面的问题: Mockito cannot mock/spy because : - final class 问题重现: 引入该依赖到项目的mav ...