Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

akka http: Akka streams vs actors to build a rest service

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.

like image 818
Mutaz Avatar asked Nov 05 '15 05:11

Mutaz


People also ask

What is Akka HTTP used for?

Akka HTTP is mostly used for making integration layers based on HTTP. A user can build the application based on their requirements, and Akka HTTP can be used to cater to the needs of HTTP integration.

What is alternative to Akka?

Spring, Scala, Erlang, Kafka, and Spring Boot are the most popular alternatives and competitors to Akka.

Is Akka HTTP a framework?

Akka HTTP is not a framework–not because we don't like frameworks–but to provide maximum flexibility. For example, you might use the Play framework to implement browser-based interactions or Lagom framework for creating microservices, both of them are also based on Akka.

Is Akka HTTP async?

Akka HTTP also provides an embedded, Reactive-Streams-based, fully asynchronous HTTP/1.1 server implemented on top of Streams. It supports the following features: Full support for HTTP persistent connections.


2 Answers

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.

like image 51
Rüdiger Klaehn Avatar answered Oct 17 '22 09:10

Rüdiger Klaehn


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)
    }
  } 
like image 26
Ramón J Romero y Vigil Avatar answered Oct 17 '22 07:10

Ramón J Romero y Vigil