Статьи

Akka Notes: Основы жизненного цикла актера, часть 5

(Обратите внимание, что эта рецензия на жизненный цикл не охватывает  preRestart ни postRestart методы, ни  методы. Мы поговорим о них, когда будем обсуждать надзор)

Основной жизненный цикл актера очень интуитивно понятен. Фактически вы можете сравнить базовый жизненный цикл Actor с жизненным циклом Java-сервлета с одним специальным отличием.

  1. Как и в любом другом обычном классе, у нас есть конструктор
  2. preStart Метод вызывается обратно в следующем. Здесь вы можете инициализировать ресурсы, которые вы хотите очистить в postStop
  3. «Обслуживание» или обработка сообщений  receive методом занимает большую часть времени, и это происходит между ними.

Давайте посмотрим на простого актера, который печатает жизненный цикл.

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

}

ПРИЛОЖЕНИЕ

В  LifecycleApp только посвящённые, посылает сообщение Актера и выключит 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 — более или менее.

Причина, по которой я напечатал  context.self в конструкторе, заключается в следующем — в отличие от сервлетов, актеры имеют доступ к  ActorContext четным внутри конструктора. Разница между preStart и конструктором становится очень тонкой. Мы еще вернемся к разнице, пока будем говорить о надзоре, но, если вам интересно, preStart можно контролировать вызов,  когда перезапускается Актер (в случае сбоя). С конструктором это невозможно.

КОГДА ВЫЗЫВАЕТСЯ ПОСТПОСТ?

Как мы видели из программы,  postStop вызывается, когда ActorSystem выключается. Есть несколько других случаев, когда обратный вызов тоже вызывается.

1. ACTORSYSTEM.STOP ()

Мы могли бы остановить актера, используя  stop метод  ActorSystem и ActorContext

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) ИЛИ убивать себя без причины (это просто для развлечения. Ни один актер с амбициями не сделает этого)

class BasicLifecycleLoggingActor extends Actor with ActorLogging{


log.info ("Inside BasicLifecycleLoggingActor Constructor")
log.info (context.self.toString())
context.stop(self)
...
...
3. Пуазон

В предыдущем примере мы передали сообщение с именем stop из LifecycleApp в Actor. Актер получил это сообщение и убил себя, используя  context.stop. Мы могли бы достичь того же, передав   сообщение PoisonPill целевому субъекту . Обратите внимание, что PoisonPill сообщение, как и предыдущее сообщение об остановке, помещается в обычный почтовый ящик и будет обработано при его появлении.

object LifecycleApp extends App{

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


lifecycleActor!PoisonPill
...
...

TRIVIA

Что я имею в виду обычный почтовый ящик? Есть ли «специальный» почтовый ящик тоже? Ага. Есть. И мы поговорим об этом, когда будем говорить об этом, когда будем обсуждать вопросы надзора и  system сообщений.


ПРЕКРАЩЕНИЕ

Как только Актер остановлен, говорят, что он входит в  Terminated состояние. Непосредственный вопрос, который возникнет у вас на уме: что произойдет с сообщениями, которые отправляются действующему субъекту?

Давайте посмотрим, что:

ПРИЛОЖЕНИЕ

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. Любое сообщение, которое вы отправляете прекращенному действующему субъекту, пересылается в почтовый ящик  внутреннего действующего субъекта с  именем  DeadLetterActor .


Любопытно, что произойдет дальше?

DeadLetter Actor обрабатывает сообщения в своем почтовом ящике, упаковывает каждое сообщение как  DeadLetter  и публикует его в  EventStream.

Еще один Actor под названием  DeadLetterListener  потребляет все  DeadLetters и публикует это как сообщение журнала. Проверьте  аут .

Помните, когда мы  говорили о ведении журнала , мы видели, что все сообщения журнала публикуются в  EventStream  и что мы можем подписаться на это EventStream — просто подписчик также должен быть актером. Давайте попробуем это сейчас.

В нашем примере мы подпишемся на EventStream, следим за всеми сообщениями DeadLetter и будем печатать на консоль (так много для творчества ?? !!). Честно говоря, мы можем делать что угодно, генерируя оповещения, сохраняя их в базе данных или даже добавляя аналитику.

ПОДПИСКА НА DEADLETTERS В EVENTSTREAM
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(classOf[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])