Akka笔记之生命周期

Published: 28 Oct 2014 Category: 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方法。还有几种情况也可能会触发这个方法的调用。

  1. ActorSystem.stop()

我们可以通过ActorSystem和ActorContext的stop方法来终止一个Actor。

object LifecycleApp extends App{

  val actorSystem=ActorSystem("LifecycleActorSystem")
  val lifecycleActor=actorSystem.actorOf(Props[BasicLifecycleLoggingActor],"lifecycleActor")

  actorSystem.stop(lifecycleActor);

  ...
  ...

}
  1. 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)
  ...
  ...
  1. 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])

英文原文链接