Статьи

Акка Заметки — Актер Жизненный цикл — Базовый — 5

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

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

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

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

Тупой актер жизненного цикла

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
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.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
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()
 
 }

Выход

1
2
3
4
5
6
7
8
9
Inside BasicLifecycleLoggingActor Constructor
 
Actor[akka://LifecycleActorSystem/user/lifecycleActor#-2018741361]
 
Inside the preStart method of BasicLifecycleLoggingActor
 
hello
 
Inside postStop method of BasicLifecycleLoggingActor

В чем особая разница между сервлетами и базовым жизненным циклом актера?

Нет никакой разницы между конструктором и предварительным запуском в жизненном цикле Actor — более или менее.

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

Когда вызывается postStop?

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

1. ActorSystem.stop ()

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

01
02
03
04
05
06
07
08
09
10
11
object LifecycleApp extends App{
 
  val actorSystem=ActorSystem("LifecycleActorSystem")
  val lifecycleActor=actorSystem.actorOf(Props[BasicLifecycleLoggingActor],"lifecycleActor")
 
  actorSystem.stop(lifecycleActor);
 
  ...
  ...
 
}

2. ActorContext.stop

1) Либо посредством сообщения (извне или передачи сообщения себе)

1
2
3
4
5
6
7
class BasicLifecycleLoggingActor extends Actor with ActorLogging{ 
...
...
  def receive = LoggingReceive{
    case "hello" => log.info ("hello")
    case "stop" => context.stop(self)
  }

и

1
2
3
4
5
6
7
8
9
object LifecycleApp extends App{
 
  val actorSystem=ActorSystem("LifecycleActorSystem")
  val lifecycleActor=actorSystem.actorOf(Props[BasicLifecycleLoggingActor],"lifecycleActor")
 
 
  lifecycleActor!"stop"
...
...

2) ИЛИ убить себя без причины

(это просто для развлечения. Ни один актер с амбициями не сделает этого)

1
2
3
4
5
6
7
class BasicLifecycleLoggingActor extends Actor with ActorLogging{
 
  log.info ("Inside BasicLifecycleLoggingActor Constructor")
  log.info (context.self.toString())
  context.stop(self)
  ...
  ...

3. PoisonPill

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

1
2
3
4
5
6
7
8
object LifecycleApp extends App{
 
  val actorSystem=ActorSystem("LifecycleActorSystem")
  val lifecycleActor=actorSystem.actorOf(Props[BasicLifecycleLoggingActor],"lifecycleActor")
 
  lifecycleActor!PoisonPill
  ...
  ...

TRIVIA

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

прекращение

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

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

Приложение

01
02
03
04
05
06
07
08
09
10
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
 
}

Актер — Как и прежде

1
2
3
4
5
6
7
8
class BasicLifecycleLoggingActor extends Actor with ActorLogging{
 
  def receive = LoggingReceive{
    case "hello" => log.info ("hello")
    case "stop" => context.stop(self)
 
  }
}

Выход

1
2
3
BasicLifecycleLoggingActor - hello
 
akka.actor.RepointableActorRef - Message  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 потребляет все DeadLetter и публикует это как сообщение журнала. Проверьте это.

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

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

ПОДПИСКА НА DEADLETTERS В EVENTSTREAM

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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")
  }
}

Выход

1
2
3
4
5
164  [LifecycleActorSystem-akka.actor.default-dispatcher-4] INFO  BasicLifecycleLoggingActor - hello
 
167  [LifecycleActorSystem-akka.actor.default-dispatcher-4] INFO  akka.actor.RepointableActorRef - Message  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])
Ссылка: Akka Notes — Actor Lifecycle — Basic — 5 от нашего партнера JCG Аруна Маниваннана в блоге Rerun.me .