Akka Actors
The Play Framework is built upon Akka actors, but does everything so that you don’t really need to use them. Despite this, actors are easy to integrate with Play, precisely because it is built on them (there is already an actor system for you to use)
A few exceptions are:
- websockets handling (doing that using actors is a breeze)
- scheduling tasks for doing daily or weekly jobs
Akka actors use messages to communicate between among them. This behavior allows callers to send a message and go back to the pool of available threads to keep processing more requests.
A caller usually sends a message and does not expect a response. This is called a tell operation or !
. However, web operations usually expect a response from actors that can be used to respond to the user who started the call. This is called an ask operation or ?
.
A simple actor can look like this:
package actors
import akka.actor._
object HelloActor {
def props = Props[HelloActor]
case class SayHello(name: String)
}
class HelloActor extends Actor {
import HelloActor._
def receive = {
case SayHello(name: String) =>
sender() ! "Hello, " + name
}
}
And a simple controller that uses our actor would look like this:
package controllers
import actors.HelloActor
import akka.actor.ActorSystem
import akka.util.Timeout
import javax.inject._
import play.api._
import play.api.mvc._
@Singleton
class HomeController @Inject()(system: ActorSystem, val controllerComponents: ControllerComponents) extends BaseController {
val helloActor = system.actorOf(HelloActor.props, "hello-actor")
import scala.concurrent.duration._
import akka.pattern.ask
implicit val timeout: Timeout = 5.seconds
import scala.concurrent.ExecutionContext.Implicits.global
def sayHello(name: String) = Action.async {
(helloActor ? HelloActor.SayHello(name)).mapTo[String].map { message =>
Ok(message)
}
}
}
This controller reads a parameter from the path and sends it to the actor. Which in return will respond with a Hello
message. Such a message is going to be sent back to our user.
Some important things to notice:
- We’re using the
?
ask operator on our controller, indicating that we expect a response from our actor. - We’re using the
!
operator (tell) on our actor code to respond back to our controller. - Notice how the actor call returns a future. Hence we have to use Action.async on our controller function.
Finally an entry to the conf/routes
file has to be added so we can read a name from the path.
GET /say-hello/:name controllers.HomeController.sayHello(name: String)
References
- Lightbend. Defining Actors and messages
- Play Framework. Integrating with Akka
- Play Framework. Scheduling asynchronous tasks
- Stackoverflow. Run a function periodically in Scala
- Stackoverflow. Creating a scheduled task in scala with play 2.5