Akka笔记之生命周期
(请注意本篇中所讲的生命周期并不包括preRestart以及postRestart阶段。后续讲到监督的时候再介绍下它们)
基础的Actor生命周期是非常直观的。事实上你可以将它跟Java servlet的生命周期做一下对比,它们之间只有一处明显的不同。
- 跟别的正常的类那样,它也有一个构造方法。
- 紧接着会调用一个preStart方法。你可以在这里初始化一些资源,最后在postStop方法里面将它们给释放掉。
- 执行服务或者说处理消息是由receive方法来完成的,绝大部分运行时间都被这个方法占据了,它是在preStart与postStop之间执行的。
我们来看一个简单的Actor,它会将Actor的生命周期给打印出来。
打印生命周期的Actor
package me.rerun.akkanotes.lifecycle
import akka.actor.{ActorLogging, Actor}
import akka.event.LoggingReceive
class BasicLifecycleLoggingActor extends Actor with ActorLogging{
log.info ("Inside BasicLifecycleLoggingActor Constructor")
log.info (context.self.toString())
override def preStart() ={
log.info("Inside the preStart method of BasicLifecycleLoggingActor")
}
def receive = LoggingReceive{
case "hello" => log.info ("hello")
}
override def postStop()={
log.info ("Inside postStop method of BasicLifecycleLoggingActor")
}
}
应用
LifecycleApp会初始化一个Actor,然后给它发送一条消息,最后关掉ActorSystem。
import akka.actor.{ActorSystem, Props}
object LifecycleApp extends App{
val actorSystem=ActorSystem("LifecycleActorSystem")
val lifecycleActor=actorSystem.actorOf(Props[BasicLifecycleLoggingActor],"lifecycleActor")
lifecycleActor!"hello"
//wait for a couple of seconds before shutdown
Thread.sleep(2000)
actorSystem.shutdown()
}
输出
Inside BasicLifecycleLoggingActor Constructor
Actor[akka://LifecycleActorSystem/user/lifecycleActor#-2018741361]
Inside the preStart method of BasicLifecycleLoggingActor
hello
Inside postStop method of BasicLifecycleLoggingActor
Servlet和Actor的生命周期之间有什么明显的区别?
在Actor生命周期里的构造方法以及preStart方法中并没有什么不同——基本是一样的。
为什么我要在构造方法里打印出context.self?这是因为————Actor跟Servlet不同,即便是在构造方法里,它也能访问得到ActorContext。preStart和构造方法之间的边界变得非常模糊。后面说到监督的时候我们会说一下它们的区别,不过如果你好奇的话我可以给你先透露一下————Actor重启的时候(比如说崩溃了之后重启)通过调用preStart方法还能重新初始化。而构造方法则实现不了这点(只会初始化一次)。
何时调用postStop?
从程序中可以看到,当ActorSystem关闭的时候会调用到postStop方法。还有几种情况也可能会触发这个方法的调用。
- ActorSystem.stop()
我们可以通过ActorSystem和ActorContext的stop方法来终止一个Actor。
object LifecycleApp extends App{
val actorSystem=ActorSystem("LifecycleActorSystem")
val lifecycleActor=actorSystem.actorOf(Props[BasicLifecycleLoggingActor],"lifecycleActor")
actorSystem.stop(lifecycleActor);
...
...
}
- ActorContext.stop
1) 可以通过发消息来实现(外部消息或者内部消息都可以)
class BasicLifecycleLoggingActor extends Actor with ActorLogging{
...
...
def receive = LoggingReceive{
case "hello" => log.info ("hello")
case "stop" => context.stop(self)
}
以及
object LifecycleApp extends App{
val actorSystem=ActorSystem("LifecycleActorSystem")
val lifecycleActor=actorSystem.actorOf(Props[BasicLifecycleLoggingActor],"lifecycleActor")
lifecycleActor!"stop"
...
2) 或者二话不说地就把自己kill掉(当然这只是开玩笑的。有正经事的Actor都不会这么干的)
class BasicLifecycleLoggingActor extends Actor with ActorLogging{
log.info ("Inside BasicLifecycleLoggingActor Constructor")
log.info (context.self.toString())
context.stop(self)
...
...
- PoisonPill(毒药丸)
在前面那个例子中,LifecycleApp将一个stop消息发送给了Actor。后者接收到消息之后通过context.stop把自己给杀掉。将PoisonPill消息发送给目标Actor也可以实现这个效果。PoisonPill和前面的那个stop消息一样,它也会被扔进到一个普通邮箱里面排队,只有当轮到它的时候才会进行处理。
object LifecycleApp extends App{
val actorSystem=ActorSystem("LifecycleActorSystem")
val lifecycleActor=actorSystem.actorOf(Props[BasicLifecycleLoggingActor],"lifecycleActor")
lifecycleActor!PoisonPill
...
...
小知识
什么是普通邮箱?是不是还有“特殊”的邮箱?没错。在讲到监督和系统消息的时候我们会说到这个。
终止阶段
一旦Actor停止了,它便进入了被称之为终止的状态。你可能马上就会想到一个问题,如果消息发送到了一个已终止的Actor上的话会怎样呢?
我们来看一下:
应用
object LifecycleApp extends App{
val actorSystem=ActorSystem("LifecycleActorSystem")
val lifecycleActor=actorSystem.actorOf(Props[BasicLifecycleLoggingActor],"lifecycleActor")
lifecycleActor!"hello"
lifecycleActor!"stop"
lifecycleActor!"hello" //Sending message to an Actor which is already stopped
}
Actor——和之前一样
class BasicLifecycleLoggingActor extends Actor with ActorLogging{
def receive = LoggingReceive{
case "hello" => log.info ("hello")
case "stop" => context.stop(self)
}
}
输出
BasicLifecycleLoggingActor - hello
akka.actor.RepointableActorRef - Message [java.lang.String] from Actor[akka://LifecycleActorSystem/deadLetters] to Actor[akka://LifecycleActorSystem/user/lifecycleActor#-569230546] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
我们可以看到日志中出现了几个deadletter。所有发送到已终止的Actor的消息都会被转发给一个叫做DeadLetterActor的内部Actor。
很好奇接下来发生了些什么?
DeadLetter Actor会去处理自己邮箱里的消息,它把每条消息都封装成一个DeadLetter对象然后发布到EventStream里面去。
有一个叫做DeadLetterListener的Actor会去消费这些DeadLetter消息并把它们作为一条日志消息发布出去。看下这里。
还记得吧,akka日志那篇中曾经说过,所有的日志消息都会发布到EventStream里面,你可以随便订阅————有一个条件就是订阅者必须得是一个Actor。我们来试一下。
对于我们这个示例而言,我们要订阅到EventStream上并监听所有的DeadLetter消息然后将它们打印到控制台上(就干了这么点事?)坦白来讲,其实我们可以做很多事情,比如生成一个警告,或者把它存储到数据库里,甚至用它来进行分析。
订阅EventStream中的DeadLetter消息
import akka.actor.ActorSystem
import akka.actor.Props
import akka.actor.PoisonPill
import akka.actor.DeadLetter
import akka.actor.Actor
object LifecycleApp extends App {
val actorSystem = ActorSystem("LifecycleActorSystem")
val lifecycleActor = actorSystem.actorOf(Props[BasicLifecycleLoggingActor], "lifecycleActor")
val deadLetterListener = actorSystem.actorOf(Props[MyCustomDeadLetterListener])
actorSystem.eventStream.subscribe(deadLetterListener, classOf[DeadLetter])
lifecycleActor ! "hello"
lifecycleActor ! "stop"
lifecycleActor ! "hello"
}
class MyCustomDeadLetterListener extends Actor {
def receive = {
case deadLetter: DeadLetter => println(s"FROM CUSTOM LISTENER $deadLetter")
}
}
输出
[LifecycleActorSystem-akka.actor.default-dispatcher-4] INFO BasicLifecycleLoggingActor - hello
[LifecycleActorSystem-akka.actor.default-dispatcher-4] INFO akka.actor.RepointableActorRef - Message [java.lang.String] from Actor[akka://LifecycleActorSystem/deadLetters] to Actor[akka://LifecycleActorSystem/user/lifecycleActor#-782937925] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
FROM CUSTOM LISTENER DeadLetter(hello,Actor[akka://LifecycleActorSystem/deadLetters],Actor[akka://LifecycleActorSystem/user/lifecycleActor#-782937925])