Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Differences between Akka HTTP and Netty

Can someone please explain the major differences between Akka HTTP and Netty? Netty offers other protocols like FTP as well. Akka HTTP can be used in Scala and Java and is build on the actor model. But apart from this both are asynchronous. When would I use Akka HTTP and when Netty? What are the typical use cases for both?

like image 857
Kris Avatar asked Jun 14 '17 15:06

Kris


People also ask

Does Akka HTTP use Netty?

Akka HTTP implements a full server stack for HTTP, including full HTTPS support, and has support for HTTP/2. The Akka HTTP server backend is the default in Play. You can also use the Netty backend if you choose.

What is Akka HTTP?

Akka HTTP is a series of modules that implement a server-side and client-side HTTP stack. It is a general toolkit for providing and consuming HTTP-based services. Any interaction with a browser is part of these modules; however, it is not Akka HTTP's primary concern.

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.

What is alternative to Akka?

I think the closest product/framework to Akka is erlang language.


2 Answers

Akka HTTP Server is a HTTP and WebSocket server with high-level DSL. Netty is a low-level "asynchronous event-driven network application framework", allowing to implement any TCP/UDP protocol you need.

So, unless you need some low-level networking, you should not use plain Netty. An equivalent for Akka HTTP using Netty would be something like Netty Reactor, and a higher level on top of them could be something like Spring WebFlux.

On the other side, Akka-HTTP is based on Akka Actors, which is a framework suggesting a specific application model. Also, Akka depends on Scala, which could affect a decision if you already know Scala or if you are not ready to learn Scala when debugging your application.

like image 36
Konstantin Pelepelin Avatar answered Oct 18 '22 02:10

Konstantin Pelepelin


Here are what I see as the primary contrastable areas:

Coding Style

Let's take netty's discard server example which is presumably the easiest example given that it's the first in the documentation.

For akka-http this is relatively simple:

object WebServer {
  def main(args: Array[String]) {

    implicit val system = ActorSystem("my-system")
    implicit val materializer = ActorMaterializer()

    val route =
      extractRequestEntity { entity =>
        onComplete(entity.discardBytes(materializer)) { _ =>
          case _ => complete(StatusCodes.Ok)
        }
      }

    val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
}

For netty this is much more verbose:

public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
        // Discard the received data silently.
        ((ByteBuf) msg).release(); // (3)
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }
}

public class DiscardServer {

    private int port;

    public DiscardServer(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap(); // (2)
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class) // (3)
             .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ch.pipeline().addLast(new DiscardServerHandler());
                 }
             })
             .option(ChannelOption.SO_BACKLOG, 128)          // (5)
             .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

            // Bind and start to accept incoming connections.
            ChannelFuture f = b.bind(port).sync(); // (7)

            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new DiscardServer(8080).run();
    }
}

Directives

One of akka-http's biggest strengths, in my opinion, is Directives, which provide a DSL for complicated request handling logic. Suppose, for example, that we wanted to respond with one message for GET and PUT requests and another message for all other request methods. This is very easy using Directives:

val route = 
  (get | put) {
    complete("You sent a GET or PUT")
  } ~ 
  complete("Shame shame")

Of if you want to get an order item and quantity from a request path:

val route = 
  path("order" / Segment / IntNumber) { (item, qty) =>
    complete(s"Your order: item: $item quantity: $qty")
  }

This functionality does not exist within netty.

Streaming

One last item I would note is around streaming. akka-http is based on akka-stream. Therefore, akka-http handles the streaming nature of request entities well. Take netty's Looking Into the Received Data example, for akka this looks like

//a stream with a Source, intermediate processing steps, and a Sink
val entityToConsole : (RequestEntity) => Future[Done] =
  (_ : RequestEntity)
    .getDataBytes()
    .map(_.utf8String)
    .to(Sink.foreach[String](println))
    .run()

val route = 
  extractRequestEntity { entity =>
    onComplete(entityToConsole(entity)) { _ =>
      case Success(_) => complete(200, "all data written to console")
      case Failure(_) => complete(404, "problem writing to console)
    }
  }

Netty must handle the same problem with byte buffers and while loops:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ByteBuf in = (ByteBuf) msg;
    try {
        while (in.isReadable()) { // (1)
            System.out.print((char) in.readByte());
            System.out.flush();
        }
    } finally {
        ReferenceCountUtil.release(msg); // (2)
    }
}
like image 117
Ramón J Romero y Vigil Avatar answered Oct 18 '22 01:10

Ramón J Romero y Vigil