• [翻译]AKKA笔记


    原文地址: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])  
    

    文章来自微信平台「麦芽面包」(微信扫描二维码关注)。未经允许,禁止转载。

  • 相关阅读:
    集合类学习之ArrayList源码解析
    集合类学习之Hashmap机制研究
    基于 HTTP/2 的全新 APNs 协议
    tcp协议头窗口,滑动窗口,流控制,拥塞控制关系
    TCP、UDP、IP 协议分析
    /proc/net/tcp中各项参数说明
    linux系统limit知识
    随机行
    nf_conntrack被启用导致服务故障
    HTTP 2.0的那些事
  • 原文地址:https://www.cnblogs.com/zhukunrong/p/5596611.html
Copyright © 2020-2023  润新知