0

Currently I'm trying to implement the "actor-per-request" pattern proposed by NET-A-PORTER devs in Akka HTTP. The problem I'm facing is that this pattern is not documented anywhere in the docs. There doesn't seem to be a way to do the following:

IO(Http) ! Http.Bind(serviceActor, "localhost", port = 38080)

How can I use one Akka actor per request without using Spray?

giannoug
  • 643
  • 3
  • 8
  • 20

1 Answers1

1

The HttpExt class has a method bindAndHAndleAsync that can be used for this purpose. This method takes in a function with the following signature:

handler: (HttpRequest) ⇒ Future[HttpResponse]

So, suppose we have an Actor that will produce an HttpResponse when asked about an HttpRequest:

class HttpResponseHandlerActor extends Actor {
  override def receive = {
    case _ : HttpRequest => 
      sender() ! HttpResponse(200, entity = "Response From Actor")
  }
}

Inefficient Answer

Your question explicitly asks how to use 1 Actor per request, to do that we can now use our Actor class to create a handler function:

implicit val actorSystem = ActorSystem()

implicit val timeout = Timeout(5 seconds)

val handler : (HttpRequest) => Future[HttpResponse] = (httpRequest) = {
  val actorHandlerRef = 
    system.actorOf(Props[HttpResponseHandlerActor], "responseActor")

  (actorHandlerRef ask httpRequest).mapTo[HttpResponse]
}

We can now use this function to bind our server with:

val serverBinding : Future[ServerBinding] = 
  Http().bindAndHandleAsync(handler, "localhost", 8080)

Efficient Answer

It is usually not necessary to re-create a new Actor for each request, typically you want to create 1 Actor and use it for every request.
Therefore we can move the Actor creation outside of handler:

val handler : (ActorRef) => (HttpRequest) => Future[HttpResponse] = 
  (actorRef) => (httpRequest) =>
     (actorRef ask httpRequest).mapTo[HttpResponse]

The server binding is now slightly modified to:

val singleResponseActorRef = 
  system.actorOf(Props[HttpResponseHandlerActor], "responseActor")

val serverBinding : Future[ServerBinding] = 
  Http().bindAndHandleAsync(handler(singleResponseActorRef),
                            "localhost", 
                            8080)
Ramón J Romero y Vigil
  • 17,373
  • 7
  • 77
  • 125