Appendix B: Schedulers

Akka provides a fairly simple but capable task scheduling mechanism. The timing is based on Netty's HashedWheelTimer and uses an approach that simply looks to see which tasks are overdue each time it ticks. This does not allow for extremely precise accuracy, but it works for a large majority of cases where tasks simply need to be run on a regular basis.

The scheduler is capable of either sending a message to a designated actor, running a simple function, or running an instance of java.lang.Runnable. Further, it can perform these functions at either set intervals (along with an optional initial delay before the first execution) or after a single interval. A few simple examples will help to illuminate the workings of the scheduler.

Also, like futures, the scheduler requires an implicit ExecutionContext to be in scope. The simplest way to go about this is to simply import the actor system's dispatcher into the current scope. You can, of course, use an alternative dispatcher, as described in chapter six.

 
import akka.actor.{Actor, Props}
import scala.util.concurrent.duration._
import system.dispatcher

val actor = system.actorOf(Props(new Actor {
def receive = {
case msg => println(msg)
}
}))
system.scheduler.schedule(10 seconds, 0 seconds, actor, "Hello!")

This example above will print out the message Hello! every ten seconds starting from the point when the code is executed, since there is a zero second initial delay specified. To send the same message but without repeating and with a fifteen second delay, use the following code:

 system.scheduler.scheduleOnce(15 seconds, actor, "Hello!") 

As mentioned earlier, you can also just schedule simple functions or Runnables, as well, using either schedule or scheduleOnce.

 // execute this function every second with an initial 5 second
// delay
system.scheduler.schedule(5 seconds, 1 second) {
log.info("tick!")
}
system.scheduler.scheduleOnce(60 seconds) {
log.info("One minute is gone!")
}
val runnable = new Runnable {
def run() {
log.info("tock!")
}
}
system.scheduler.schedule(5 seconds, 1 second, runnable)
system.scheduler.scheduleOnce(60 minutes) {
log.info("Another hour gone.")
}

Each of these calls to schedule or scheduleOnce return a Cancellable object, which defines the cancel and isCancelled methods. You can cancel a scheduled task using cancel and check to see if it has already been cancelled using isCancelled.

I've found that it's often useful to have an actor perform some tasks at a regular interval. A first approach to implementing this would likely involve creating the actor and then sending a message (we'll call it Tick for this scenario) to the actor at specified intervals using schedule. The problem with this approach is that if the actor wants to interrupt the scheduled ticking, it needs to have a reference to the Cancellable object for that task. But this can sometimes be awkward to do in a clean manner.

A cleaner approach that gives you more control is to use scheduleOnce and then, in the block that handles the Tick message, have it schedule the next Tick.

 
import akka.actor.{Actor, Props}
import scala.concurrent.duration._

case object Tick

class TickingActor extends Actor {

// shortcut
val system = context.system
override def preStart() = {
system.scheduler.scheduleOnce(1 second, self, Tick)
}

def receive = {
case Tick => {
system.scheduler.scheduleOnce(1 second, self, Tick)
// do whatever work needs to be done
}
}
}

If you find for some reason you need to wait to schedule the next Tick message until after the work has been done, you should use a try/finally structure and place the call to scheduleOnce inside the finally block to prevent it from failing to get scheduled should any errors occur.

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset