Актер, контролируемый BackoffSupervisor, теряет спрятанные сообщения после перезапуска
У меня есть актер с использованием тайника. Иногда, когда он падает, он теряет все спрятанные сообщения. Я обнаружил, что это зависит от того, какую логику наблюдения я использую.
Я написал простой пример.
Актер с заначкой:
case object WrongMessage
case object TestMessage
case object InitialMessage
class TestActor extends Actor with Stash {
override def receive: Receive = uninitializedReceive
def uninitializedReceive: Receive = {
case TestMessage =>
println(s"stash test message")
stash()
case WrongMessage =>
println(s"wrong message")
throw new Throwable("wrong message")
case InitialMessage =>
println(s"initial message")
context.become(initializedReceive)
unstashAll()
}
def initializedReceive: Receive = {
case TestMessage =>
println(s"test message")
}
}
В следующем коде TestActor
никогда не получает тайник TestMessage
:
object Test1 extends App {
implicit val system: ActorSystem = ActorSystem()
val actorRef = system.actorOf(BackoffSupervisor.props(Backoff.onFailure(
Props[TestActor], "TestActor", 1 seconds, 1 seconds, 0
).withSupervisorStrategy(OneForOneStrategy()({
case _ => SupervisorStrategy.Restart
}))))
actorRef ! TestMessage
Thread.sleep(5000L)
actorRef ! WrongMessage
Thread.sleep(5000L)
actorRef ! InitialMessage
}
Но этот код работает хорошо:
class SupervisionActor extends Actor {
val testActorRef: ActorRef = context.actorOf(Props[TestActor])
override def supervisorStrategy: SupervisorStrategy = OneForOneStrategy()({
case _ => SupervisorStrategy.Restart
})
override def receive: Receive = {
case message => testActorRef forward message
}
}
object Test2 extends App {
implicit val system: ActorSystem = ActorSystem()
val actorRef = system.actorOf(Props(classOf[SupervisionActor]))
actorRef ! TestMessage
Thread.sleep(5000L)
actorRef ! WrongMessage
Thread.sleep(5000L)
actorRef ! InitialMessage
}
Я посмотрел на источники и обнаружил, что наблюдение за актором использует метод LocalActorRef.restart, который поддерживается логикой системного диспетчера, но BackoffSupervisor
просто создает нового актера после прекращения действия старого. Есть ли способ обойти это?
1 ответ
Я не уверен, что можно сделать restart
под BackoffSupervisor
правильно отправлять спрятанные сообщения без особых усилий по повторной реализации.
Как вы уже отметили, что BackoffSupervisor
делает свое restart
это обходит стандартный жизненный цикл актера. Фактически, это явно отмечено в исходном коде BackoffOnRestartSupervisor:
Какой бы ни была окончательная Директива, мы переведем все Перезапуски в наши собственные Перезапуски, которые предполагают остановку ребенка.
Если вы не читали об этой сообщенной проблеме, у нее есть соответствующее обсуждение: проблема с Backoff.onFailure.
Backoff.onStop также предоставляет желаемую функцию BackoffSupervisor, но, к сожалению, он имеет свои собственные сценарии использования и не будет вызван исключением.