Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to respond with the result of an actor call?

We are looking at using Akka-HTTP Java API - using Routing DSL.

It's not clear how to use the Routing functionality to respond to an HttpRequest; using an Untyped Akka Actor. For example, upon matching a Route path, how do we hand off the request to a "handler" ActorRef, which will then respond with a HttpResponse in a asynchronous way?

A similar question was posted on Akka-User mailing list, but with no followup solutions as such - https://groups.google.com/d/msg/akka-user/qHe3Ko7EVvg/KC-aKz_o5aoJ.

like image 309
Piyush Jajoo Avatar asked Jun 19 '15 04:06

Piyush Jajoo


People also ask

Which method is used to stop the actor?

Actors are stopped by invoking the stop method of a ActorRefFactory, i.e. ActorContext or ActorSystem. Typically the context is used for stopping child actors and the system for stopping top level actors. The actual termination of the actor is performed asynchronously, i.e. stop may return before the actor is stopped.

What is ActorRef in Akka?

ActorRefFactory, an interface which is implemented by ActorSystem and akka. actor. ActorContext. This means actors can be created top-level in the ActorSystem or as children of an existing actor, but only from within that actor. ActorRefs can be freely shared among actors by message passing.

What is Akka protocol?

Interacting with an Actor in Akka is done through an ActorRef[T] where T is the type of messages the actor accepts, also known as the “protocol”. This ensures that only the right kind of messages can be sent to an actor and also that no one else but the Actor itself can access the Actor instance internals.


2 Answers

This can be accomplished with a combination of the onComplete directive and the ask pattern.

In the below example the RequestHandlerActor actor is used to create a HttpResponse based on the HttpRequest. This Actor is asked from within the route.

I have never used Java for routing code so my response is in Scala.

import scala.concurrent.duration._
import akka.actor.ActorSystem
import akka.http.scaladsl.model.HttpResponse
import akka.http.scaladsl.model.HttpRequest
import akka.actor.Actor
import akka.http.scaladsl.server.Directives._
import akka.actor.Props
import akka.pattern.ask
import akka.util.Timeout
import scala.util.{Success, Failure}
import akka.http.scaladsl.model.StatusCodes.InternalServerError

class RequestHandlerActor extends Actor {
  override def receive = {
    case httpRequest : HttpRequest =>
      sender() ! HttpResponse(entity = "actor responds nicely")
  }
}

implicit val actorSystem = ActorSystem()
implicit val timeout = Timeout(5 seconds)

val requestRef = actorSystem actorOf Props[RequestHandlerActor]

val route = 
  extractRequest { request =>
    onComplete((requestRef ? request).mapTo[HttpResponse]) {
      case Success(response) => complete(response)
      case Failure(ex) => 
        complete((InternalServerError, s"Actor not playing nice: ${ex.getMessage}"))
    } 
  }

This route can then be used passed into the bindAndHandle method like any other Flow.

like image 102
Ramón J Romero y Vigil Avatar answered Oct 18 '22 21:10

Ramón J Romero y Vigil


I have been looking the solution to the same problem as described by the author of the question. Finally, I came up to the following Java code for route creation:

    ActorRef ref = system.actorOf(Props.create(RequestHandlerActor.class));

    return get(() -> route(
            pathSingleSlash(() ->
                    extractRequest(httpRequest -> {
                        Timeout timeout = new Timeout(Duration.create(5, TimeUnit.SECONDS));
                        CompletionStage<HttpResponse> completionStage = PatternsCS.ask(ref, httpRequest, timeout)
                                .thenApplyAsync(HttpResponse.class::cast);

                        return completeWithFuture(completionStage);
                    })
            ))
    );

And RequestHandlerActor is:

public class RequestHandlerActor extends UntypedActor {
    @Override
    public void onReceive(Object msg) {
        if (msg instanceof HttpRequest) {
            HttpResponse httpResponse = HttpResponse.create()
                    .withEntity(ContentTypes.TEXT_HTML_UTF8,
                            "<html><body>Hello world!</body></html>");

            getSender().tell(httpResponse, getSelf());
        } else {
            unhandled(msg);
        }
    }
}
like image 1
tysonite Avatar answered Oct 18 '22 22:10

tysonite