I am pretty new to the Akka world and I have to migrate a project from Spray to akka-http.
In spray, a route was of type Route = RequestContext ⇒ Unit
.
But in akka-http, it is of type Route = RequestContext ⇒ Future[RouteResult]
.
So in spray, we would often handle and complete our requests through a chain of Actors (outside of the main controller) using only fire-and-forget so we didn't have to "ask" and the performance was great. Now, we have to use "ask" every time we pass the request to another Actor (correct me if I'm wrong)
I've been searching a lot and I found a few options which I'm not sure if they fully satisfy me (the need to complete a request in another Actor without the need to return it back to the controller). So that's where you could help me :)
Option 1: Using onSuccess
/onComplete
Does using this block my main controller from receiving more requests?
Option 2: Using Future
s and using RouteResult.Complete
I've found the following example at How to complete a request in another actor when using akka-http:
import akka.actor.{ActorSystem, Actor, Props}
import akka.pattern.ask
import akka.stream.ActorMaterializer
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.{RequestContext, RouteResult}
import scala.concurrent.Future
import akka.http.scaladsl.model.HttpResponse
import akka.util.Timeout
class RequestActor extends Actor {
//business logic - returns empty HttpResponse
def handleRequestMessage(requestContext : RequestContext) =
RouteResult.Complete(new HttpResponse())
override def receive = {
case reqContext : RequestContext =>
sender ! handleRequestMessage(reqContext)
}
}//end class RequestActor
object RouteActorTest extends App {
implicit val as = ActorSystem("RouteActorTest")
implicit val timeout = new Timeout(1000)
val actorRef = as actorOf Props[RequestActor]
def handleRequest(reqContext : RequestContext) : Future[RouteResult] =
ask(actorRef,reqContext).mapTo[RouteResult]
val route = path("") { get(handleRequest) }
//rest of application...
}//end object RouteActorTest
But this actually passes the response back every time to the previous Actor (the sender) until it reaches the main controller. Another thing about this option is that in the code, it says:
/**
* The result of handling a request.
*
* As a user you typically don't create RouteResult instances directly.
* Instead, use the methods on the [[RequestContext]] to achieve the desired effect.
*/
So I'm not sure if it's a recommended way of doing it.
I've tried using requestContext.complete() in another actor but it doesn't work (no error thrown, simply doesn't send response) Does anybody know what the best way is to implement the previous architecture we had?
Thanks a lot!
You don't need to complete the request in another actor. All you need to do is for the actor that handles the request to send back a result, and you can use
complete
to signal completion of the future:And route:
If you want to handle to handcraft the
HttpResponse
yourself, you can always usecontext.complete
: