22
votes

When it comes to creating a REST web service with 60+ API on akka http. How can I choose whether I should go with akka streams or akka actors? In his post, Jos shows two ways to create an API on akka http but he doesn't show when I should select one over the other.

2

2 Answers

23
votes

This is a difficult question. Obviously, both approaches work. So to a certain degree it is a matter of taste/familiarity. So everything following now is just my personal opinion.

When possible, I prefer using akka-stream due to its more high-level nature and type safety. But whether this is a viable approach depends very much on the task of the REST API.

Akka-stream

If your REST API is a service that e.g. answers questions based on external data (e.g. a currency exchange rate API), it is preferable to implement it using akka-stream.

Another example where akka-stream would be preferable would be some kind of database frontend where the task of the REST API is to parse query parameters, translate them into a DB query, execute the query and translate the result according to the content-type requested by the user. In both cases, the data flow maps easily to akka-stream primitives.

Actors

An example where using actors would be preferable might be if your API allows querying and updating a number of persistent actors on a cluster. In that case either a pure actor-based solution or a mixed solution (parsing query parameters and translating results using akka-stream, do the rest using actors) might be preferable.

Another example where an actor-based solution might be preferable would be if you have a REST API for long-running requests (e.g. websockets), and want to deploy the processing pipeline of the REST API itself on a cluster. I don't think something like this is currently possible at all using akka-stream.

Summary

So to summarize: look at the data flow of each API and see if it maps cleanly to the primitives offered by akka-stream. If this is the case, implement it using akka-stream. Otherwise, implement using actors or a mixed solution.

11
votes

Don't Forget Futures!

One addendum I would make to Rudiger Klaehn's fine answer is to also consider the use case of a Future. The composability of Futures and resource management of ExecutionContext make Futures ideal for many, if not most, situations.

There is an excellent blog post describing when Futures are a better choice than Actors. Further, the back-pressure provided by Streams comes with some pretty hefty overhead.

Just because you're down the rabbit hole using akka-http does not mean all concurrency within your request handler has to be confined to Actors or Streams.

Route

Route inherently accomodates Futures in the type definition:

type Route = (RequestContext) ⇒ Future[RouteResult]

Therefore you can bake a Future directly into your Route using only functions and Futures, no Directives:

val requestHandler : RequestContext => HttpResponse = ???

val route : Route = 
  (requestContext) => Future(requestHandler(requestContext)) map RouteResult.Complete

onComplete Directive

The onComplete Directive allows you to "unwrap" a Future within your Route:

val route = 
  get {

    val future : Future[HttpResponse] = ???

    onComplete(future) {
      case Success(httpResponse) => complete(httpResponse)
      case Failure(exception)    => complete(InternalServerError -> exception.toString)
    }
  }