简体   繁体   中英

How to know if an actor is idle

I have an unknown number of jobs to be executed by a known (of course) number of actors. The initial jobs number might increase after an actor completes a job. That is, an actor, upon completion of its task, might add a new job to be executed.

The way I am handling this is to have each actor send a message back to the master when it completes its job, not only with the result of the execution but also with a 'flag' indicating that the actor is now idle. The master has a queue of jobs and a queue of idle actors, whenever an actor sends a 'job completed message' the master will check if there's something else for that actor to do... so on and so forth until the jobs' queue is empty and the idles' queue is full... at that point I shutdown the system. There's no much supervision here, so I feel that I am not doing it properly...

I am not using a router because I could not find a way to query the router for idle actors, so my question is:

What is the 'proper' way to handle the situation I have described above in Akka?

You should take a look at Akka's routing capabilites. SmallestMailboxRouter might be what you are looking for.

As an alternative, you could just create actors on demand, ie for every task, a new actor is created dynamically. A central actor keeps track of all the actors that are currently active. Once a worker actor is done, it sends itself a PoisonPill and informs the master about its shutdown (actively, or via the standard Terminate message that Akka will send to the supervising actor). Once there are no more active actors, ie no more tasks, the controler actor shuts down the system.

Addition after reading the comment: Take a look at the sources of SmallestMailboxLike , a Scala trait mixed in by SmallestMailboxRouter . Warning: you should have a basic knowledge of Scala. But this is generally a good idea anyway if you want to use Akka... The method isProcessingMessage(ActorRef) can be understood as isNotIdle(ActorRef)

// Returns true if the actor is currently processing a message.
// It will always return false for remote actors.
// Method is exposed to subclasses to be able to implement custom
// routers based on mailbox and actor internal state.
protected def isProcessingMessage(a: ActorRef): Boolean = a match {
  case x: LocalActorRef ?
    val cell = x.underlying
    cell.mailbox.isScheduled && cell.currentMessage != null
  case _ ? false
}

// Returns true if the actor currently has any pending messages
// in the mailbox, i.e. the mailbox is not empty.
// It will always return false for remote actors.
// Method is exposed to subclasses to be able to implement custom
// routers based on mailbox and actor internal state.
protected def hasMessages(a: ActorRef): Boolean = a match {
  case x: LocalActorRef ? x.underlying.mailbox.hasMessages
  case _                ? false
}

Another strategy can be to use a BalancingDispatcher and a RoundRobinRouter (as an actor "pool"). From Akka docs:

BalancingDispatcher
# This is an executor based event driven dispatcher that will try to redistribute work from busy actors to idle actors.




# All the actors share a single Mailbox that they get their messages from.

It is assumed that all actors using the same instance of this dispatcher can process all messages that have been sent to one of the actors; i.e. the actors belong to a pool of actors, and to the client there is no guarantee about which actor instance actually processes a given message.

# Sharability: Actors of the same type only

# Mailboxes: Any, creates one for all Actors

# Use cases: Work-sharing

Define your dispatcher in application.conf or load it programmatically on startup.

private final static Config akkaConfig = ConfigFactory.parseString(

            "my-dispatcher.type = BalancingDispatcher \n" +
            "my-dispatcher.executor = fork-join-executor \n" +
            "my-dispatcher.fork-join-executor.parallelism-min = 8 \n" +
            "my-dispatcher.fork-join-executor.parallelism-factor = 3.0 \n" +
            "my-dispatcher.fork-join-executor.parallelism-max = 64 "
);

Then define the router and dispatcher for the routees.

getContext().actorOf(new Props(MyActor.class).withRouter(new RoundRobinRouter(10)).withDispatcher("my-dispatcher"), "myActor");

So the router will simply go on "distributing" messages, and the dispatcher will run a selected actor (and it implements work stealing as well)

Balancing dispatcher will use only one mailbox for all the created actors which are all created with BalancingDispatcher. So it'll make your job simple.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM