avatar

Andres Jaimes

Akka Actors

By Andres Jaimes

- 2 minutes read - 413 words

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