1. Introduction
The Akka Actor System provides Akka Scheduler for managing the periodic execution of tasks. In this tutorial, we’ll see how we can schedule tasks using Akka Scheduler.
2. Dependency
Let’s add the Akka-actor dependency to our project:
libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % "2.6.8"
3. Single Execution Scheduler
A single execution scheduler lets us defer the execution of a task. The task will execute after the configured delay.
Let’s see how we can create a single execution scheduler.
For this tutorial, we can create a simple Actor as:
case class Greet(to: String, by: String)
case class Greeted(msg: String)
class Greetings extends Actor {
override def receive: Receive = {
case greet: Greet =>
sender() ! Greeted(s"${greet.by}: Hello, ${greet.to}")
}
}
Also, we need to initialize an ActorSystem:
val schedulerActorSystem = ActorSystem("akka-scheduler-system")
After that, we can create an ActorRef for our Greetings actor:
val greeter = schedulerActorSystem.actorOf(Props(classOf[Greetings]))
val greeting = Greet("Detective","Lucifer")
schedulerActorSystem.scheduler.scheduleOnce(5.seconds, greeter, greeting)
The method scheduleOnce takes three parameters:
- duration (delay) after which the task will execute
- actor to send the message to
- message to send
In the above example, the scheduler will send the message greet to the actor greeter after 5 seconds. We need to remember to provide an implicit ExecutionContext for the execution.
Alternatively, we can execute the above task by using the Runnable interface.
We’ll implement the task details in the run method, which will execute after the configured delay:
system.scheduler.scheduleOnce(5.seconds, new Runnable {
override def run(): Unit = greeter ! greet
})
4. Periodic Execution
Using the scheduler, we can create recurring tasks and execute them easily.
For example, we can send a greet message to the Greetings actor once per second after an initial delay of 100 milliseconds:
system.scheduler.schedule(100.millis, 1.seconds, greeter, greet)
Alternatively, we can use the Runnable interface to create a periodic schedule:
system.scheduler.schedule(10.millis, 250.millis, new Runnable {
override def run(): Unit = greeter ! greet
})
5. Types of Delays
Akka Scheduler provides two types of scheduling delays: fixed-delay execution and fixed-rate execution.
5.1. Fixed-Delay
In fixed-delay execution, the delay between subsequent executions will always be at least the given interval value. The next execution time is calculated only after the execution of the current one. The next execution will delay if the ongoing task is a long-running one. As a result, the time difference between two subsequent executions may not be constant.
From Akka version 2.6, the schedule method is deprecated, and Akka suggests that we use scheduleWithFixedDelay instead.
So, we can rewrite the previous example to use scheduleWithFixedDelay:
system.scheduler.scheduleWithFixedDelay(10.millis, 250.millis, greeter, greet)
5.2. Fixed-Rate
Fixed-rate execution will adjust the delay of subsequent tasks if the ongoing task is taking more time.
For instance, let’s say that the scheduled delay between executions is 500 milliseconds, and the current execution takes 200 milliseconds. Then the next execution will occur after 300 milliseconds (500ms – 200ms).
For creating a fixed-rate scheduler, we can use the scheduleAtFixedRate method:
system.scheduler.scheduleAtFixedRate(10.millis, 500.millis)(new Runnable {
override def run(): Unit = greeter ! greet
})
6. Canceling a Scheduler
When we create a schedule, it returns a Cancellable instance. We can use this instance to cancel an active scheduler:
val schedulerInstance:Cancellable = system.scheduler.schedule(
100.millis, 1.seconds, greeter, greet)
schedulerInstance.cancel()
7. Actor Timers
If the scheduling is done within an Actor, Akka suggests using the Actor Timers instead of Scheduler. An Actor Timer will take care of the scheduling when the actor restarts.
By using Scheduler, the lifecycle of messages can be difficult to manage. We can create a Timer by implementing the akka.actor.Timers trait:
class TimerActor(replyTo: ActorRef) extends Actor with Timers {
override def preStart(): Unit = {
timers.startTimerWithFixedDelay(PeriodicTimerKey, PeriodicTick, 200.millis)
super.preStart()
}
}
8. Scheduler Accuracy
Akka Scheduler is designed for high throughput of messages. However, it’s not very precise. There’s no guarantee that the task will execute at the exact time we provide, so the scheduler might execute after a few milliseconds of the scheduled delay.
Also, we can’t use it for executing tasks at a particular time. We can either use Quartz Scheduler or Akka Quartz Scheduler in such cases.
9. Conclusion
In this article, we’ve seen how to schedule some tasks for a future time using Akka Schedulers.
The sample code in this tutorial and relevant tests are available over on GitHub.