close
文章出處

原文地址:http://rerun.me/2014/10/21/akka-notes-actor-lifecycle-basic/

(請注意這了討論的生命周期并不包括 preRestart 或者postRestart方法,當我們討論supervision時候我們會說這個)

基本的Actor生命周期很直觀。除了一點小不同,你可以直接拿基本Actor生命周期與Java Servlet生命周期作比較。

  1. 像其他常規類一樣,我們有一個構造函數。
  2. preStart方法會被調用。 這里你可以在postStop初始化一些稍后你想清理的資源。
  3. receive方法用作服務或者消息處理,占用了大部分時間。

先看下一個打印了生命周期的簡單actor。

DUMB LIFECYCLE 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")
      }
    
    }

APP

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  

那個在基礎Actor生命周期與Servlet生命周期的一點不同是什么?

Actor生命周期中的構造函數和preStart是沒什么不一樣的。

我把context.self在構造函數中進行打印的原因就是 - 不像Servlets,Actor在構造函數中可以訪問到ActorContext。而preStart與構造函數間的差別就很微妙了。如果你要打破砂鍋問到底,我們再看下之前說的不同 - 當Actor重啟時(失敗的case)調用preStart是可控的。 用構造函數就不可能了。

什么時候POSTSTOP會被調用?

像我們前面看到的程序, postStrop在ActorSystem關閉時會被調用。還有很多其他的機會能調用到這個回調。

1.ACTORSYSTEM.STOP()

我們可以用ActorSystem的stop方法來停止一個ActorActorContext

object LifecycleApp extends App{

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

  actorSystem.stop(lifecycleActor);

  ...
  ...

} 

2.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)或無原因的把自己殺掉(這只是為了好玩。沒有一個有追求的Actor會這么做)

class BasicLifecycleLoggingActor extends Actor with ActorLogging{

  log.info ("Inside BasicLifecycleLoggingActor Constructor")
  log.info (context.self.toString())
  context.stop(self)
  ...
  ...

3.毒藥

在之前的例子,我們從LifecycleApp給Actor傳了一個叫stop的消息。Actor在收到消息后用context.stop把自己殺掉。我們也可以通過傳遞一個毒藥(PoisonPill)消息到目標actor來達到同樣的目的。請記住這個毒藥消息,會像前面的stop消息一樣被放在常規mailbox中,當被處理到的時候才會運行。

object LifecycleApp extends App{

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

  lifecycleActor!PoisonPill
  ...
  ...

4.KILL

除了發送一個毒藥(PoisonPill), 你也可以給目標Actor發送一個kill消息。

lifecycleActor ! Kill  

發送毒藥消息和kill消息,區別很微妙但很重要。

  1. PoisonPill,一個Terminated消息會被發送到所有的watcher(稍后我們會在DeathWatch章節中看到)
  2. 發送kill消息,宿主Actor會拋出一個ActorKilledException并被發送給Supervisor(稍后我們會在Supervision章節中看到)

細枝末節

我前面說的常規mailbox是啥意思?是否還有個“特別”mailbox?是的,確實有!我們會在討論supervision和system消息時說到這個。


TERMINATION

當Actor停止時,他會進入一個Terminated狀態。你馬上就會想到一個問題,那些發到一個已經是terminated狀態的Actor的消息會怎么樣?

讓我們看看:

APP

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'.  

從日志中可以看到,這里對deadletters有一些引用。任何你發給那個已經terminated的Actor的消息都會轉發給一個叫DeadLetterActor的內部Actor的mailbox。


那么這之后又發生了什么?

DeadLetter Actor處理它mailbox里的消息,把每個消息都封裝成一個DeadLetter并且把它發布到EventStream中。

另一個叫DeadLetterListener的Actor消費所有的DeadLetter并把它輸出成日志消息。從這里看

記住,當我們說日志的時候,我們可以看到所有輸出到EventStream的消息并且可以隨意消費 - 只是這個消費者一樣必須是個Actor。讓我們試試。

在我們的例子中,我們消費EventStream并且觀看所有DeadLetter消息最后打印到console(這沒有創造力??)當然,我們還能自由的做任何事如生成告警,把它保存到數據庫或把它拿去作分析。

訂閱EVENTSTREAM的DEADLETTERS

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")
  }
}

輸出

164  [LifecycleActorSystem-akka.actor.default-dispatcher-4] INFO  BasicLifecycleLoggingActor - hello 

167  [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])  

文章來自微信平臺「麥芽面包」(微信掃描二維碼關注)。未經允許,禁止轉載。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 AutoPoster 的頭像
    AutoPoster

    互聯網 - 大數據

    AutoPoster 發表在 痞客邦 留言(0) 人氣()