Akka Scala actor scheduled message does not appear

2019-07-21 07:11发布

问题:

In experimenting with Akka actors, I cooked up a use case which doesn't quite work as expected. I've defined an actor, MyActor which receives a "start" message. This message kicks off a scheduler which should relays a message "request" to it self every 1 second. Here's the code:

import akka.actor._
import scala.concurrent.Await
import scala.concurrent.duration._
import akka.pattern.gracefulStop
import scala.concurrent.ExecutionContext.Implicits.global

object ScheduledActors {

  def main(args: Array[String]) {

    implicit val system = ActorSystem("ScheduledActors")
    val admin = system.actorOf(Props[MyActor], name = "my-actor")

    admin ! "start"

    val adminFuture = gracefulStop(admin, 2 minutes)
    Await.result(adminFuture, 2 minutes)
  }

class MyActor extends Actor {

  var cancellableOption: Option[Cancellable] = None

  def receive = {

    case "start" =>
      println("start")

      cancellableOption =
        Some(context.system.scheduler.schedule(0 seconds, 1 second) { self ! "request" })

    case "request" =>
      println("request")

    case "stop" =>
      println("stop")

      cancellableOption match {
        case None => // no-op
        case Some(v) => v.cancel()
      }

      context.stop(self)
    }
  }
}

I've experimented with a handful of variants. Leveraging the schedule method variant with an explicit ActorRef and message. E.g.:

schedule(initialDelay: Duration, frequency: Duration, receiver: ActorRef, message: Any): Cancellable

In each case, the "request" message appears to never have been received by the actor. The block is most certainly called as I've verified with println statements as a sanity check. But delivery is a no-go.

This lead me to consider the self reference might not provide the same context when actually executed in this context (à la ForkJoinPool invocation). The Akka documentation does warn against using unstable references in the context of a schedule invocation. However self as a receiver is explicitly cited as a reasonable alternative.

I'm at a bit of a loss here at this point. Any help is greatly appreciated.

回答1:

It's because the actor is already terminated. When running, you should see dead letters logged, e.g.:

[INFO] [04/08/2014 17:05:03.903] [ScheduledActors-akka.actor.default-dispatcher-2] [akka://ScheduledActors/user/my-actor] Message [java.lang.String] from Actor[akka://ScheduledActors/user/my-actor#-1824493491] to Actor[akka://ScheduledActors/user/my-actor#-1824493491] 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'.

GracefulStop does not know about the scheduling you have done and stops the actor because the mailbox is empty. If you would change the main method to the following, you should see the messages:

implicit val system = ActorSystem("ScheduledActors")
val admin = system.actorOf(Props[MyActor], name = "my-actor")

admin ! "start"

Thread.sleep(5000)
admin ! "stop"


标签: scala akka