Showing posts with label akka. Show all posts
Showing posts with label akka. Show all posts

Sep 24, 2018

Generating akka-http endpoints from protobuf declarations

When I started working with akka-http last winter I was surprised they had no better alternative to hand-crafting RESTful endpoints. The gRPC Gateway prototype inspired me to some experimentation. Back then I was still hoping to convince people at work to avoid REST. Recently I wanted to share what I had learned with a colleague and it pushed me to finish the first actually working version.

There are a couple interesting tidbits for protobuf connoisseurs in general. I picked them up while digging into the gateway project. To begin with, the protobuf specification has a description written in protobuf. It is important because the protoc compiler supports plugins which in turn operate in terms of those meta-level protobufs. So you don't have to start your protobuf transpiler from scratch. The ScalaPB project did all the hard work of integrating with protoc toolchain. I am still afraid of my SBT file which I had to mostly borrow.

Secondly, the protobuf specification defines a somewhat obscure feature known as extensions. GOOG itself used an extension to define a mapping of RESTful API elements onto protobuf declarations. It was a pleasant surprise to finally find a standard. You take a protobuf service declaration, augment it with an option clause following clear guidelines, and all of a sudden your still valid protobuf file can be a player in the RESTful world too.

All that remains is to walk an AST comprised of protobuf descriptor classes and generate some code.  On the one hand, it easier than writing a query parser. On the other hand dealing mostly with Strings feels less structured. The current approach was borrowed from the gateway project. I am unhappy with its structure but don't have a good enough alternative yet. Nevertheless the code is straightforward and can be extended.

In our case the generated code will be a skeleton of akka-http service. It starts with some boilerplate Akka initialization and then generates HTTP request processing directives. To illustrate the use of the code generator there is a small sample service project. It's pretty self-explanatory once you look at the protobuf file

I learned a few things from this PoC:

  • the protoc toolchain is not particularly nice to JVM-based code; thank God for ScalaPB but your SBT file will be rather cryptic anyway
  • now that we know how to write a scalac plugin the actual transpiler logic is quite easy to write
  • the protobuf-to-REST mapping is very reasonable but has edge cases such as nested messages
One point of contention is streaming. It's well known that gRPC streaming is not synonymous with bulk data transfer. The former operates on individual messages, the latter deals with sending a large file without buffering the entire file in a byte array. The former is represented by some ScalaPB-generated case classes, the latter typically looks like an akka-http Source. So there is no standard way to express in protobuf the need to generate anything akka-streaming-related.

Mar 28, 2018

Integrating AWS SDK v2 with Akka streams

It's been a while since I last used AWS S3 a lot. In the mean time AWS came up with a new SDK and I started working with Scala and Akka full time. The SDK is still in beta but it's hard to imagine it'll take them more than a few more months to finish. Akka is still convenient but choke-full of APIs at different levels of complexity. Among other things I am researching how some of them could play together nicely. While digging, invariably a nugget or two can be found.

I wanted to plug S3 file download functionality from the SDK into a code path based on Akka streams. The former is all about CompletableFutures, the latter is about Source/Sink abstractions wired together with mostly built-in stages. Both are relatively large and self-sufficient frameworks with their own thread pools and coding idioms. I found one easy way to make them work together.

The trick is to notice that the AsyncResponseHandler returns a reactive stream Publisher. Yes, a third famous API in a file of less than a hundred lines. Akka can create a Source form a Publisher. It was also an unusual opportunity to use a Scala Promise (a quick reminder - in Scala a Promise provides write access to the state represented by a Future for read access). The idea is
  • as is custom in Scala, a time consuming operation is wrapped into a Future
  • a Promise is created before a download operation is started
  • when the download operation is started, the Promise is used to create a Future that is returned to the client
  • when the download operation finishes, the Promise is completed successfully
  • there is also the error case where the Promise is used to convey the error to the client

A unit test illustrates how a created Akka Source can be used to actually transfer some data using built-in Akka file Sink. I use a small S3 file made publicly available by some kind strangers.  

My first impression from this exercise is that it is reasonable but feels a little unnatural. Yes, the code itself is quite concise and the performance penalty must be negligible. But the j.u.c ecosystem is mostly alien to idiomatic Scala.  

Jan 9, 2018

Asynchronous RPC server with Akka HTTP

At work my team maintains a number of RESTful services. The good news is that they are implemented with akka-http which makes things tolerable. The bad news is that it's all about REST and JSON. So we are having a lively debate about a possible migration to something more like gRPC.

Personally, I am not a fan of non-binary protocols and data formats. At least partially it is related to my experience in building analytics systems where any query routinely requires a lot of data. Yes, for the time being you have to use REST/JSON to communicate between the browser and your API Gateway. But using the same approach to make your backend services talk to each other never made sense to me. I am really looking forward to the day when WASM/HTTP2&Co finally make front end development great again.

In the mean time I would like to see if I can gather enough evidence in favor of my position. So in the spirit of my recent gRPC experiment I am going to implement basically the same API with REST. As a bonus, I want to mention a couple of observations I made using akka-http. As usual, the code is on github. I continue the tradition of implementing a service with two endpoints. I even mostly follow the same code structure as before with gRPC.


The picture above sketches high-level interactions. There are a few noteworthy Akka-related details inside but nobody will be surprised by another RESTful service implementation.
  • TestServiceA - is where the actual logic would be. It also hosts a trait and case classes which would be generated from a protobuf file in case of gRPC
  • HttpEndpointA - an actual HTTP endpoint that plugs service logic into HTTP network endpoint. A good example of Akka HTTP expressiveness.
  • AkkaHttpServer - the service implementation entry point. In typical Scala fashion it directly implements the main method and performs basic Akka dependency wiring. It is also responsible for binding request handlers to the underlying HTTP server.
  • Http - technically an object in a high-level Akka API it represents the usual functionality of request/response-style interactions over HTTP 
  • AkkaHttpClient - not shown; wraps akka-http-based client logic. I spent some time getting it right because client-side examples are harder to find. In particular it illustrates how to use Spray JSON marshaller. But it could be any HTTP client capable of sending a POST request with a JSON body. 
If you are curious enough to look at the code I would like to highlight a few details useful in real life. Not all of them are prominent enough in usual examples. The HttpEndpointA class would be a good illustration of most of them.
  • ExceptionHandler - you can configure a handler for exceptions uncaught otherwise. It is the last line of defense against unexpected problems. 
  • RejectionHandler - such a handler is very useful if you want to explicitly deal with invalid requests (e.g. garbled JSON or more likely a JSON body representing a request to some other service)
  • request timeout - even though Akka has a default global property ("akka.http.server.request-timeout") you could configure it explicitly for a service that is expected to take an unusually long time 
  • implicit JSON (de)serialization with Spray - it's very convenient to use but rather puzzling initially to configure. In addition to the marshaller object holding implicit vals notice how the "import ServiceAJsonMarshaller" statement makes it usable by "as" and "complete" directives
  • composing multiple Routes - different styles are possible here; if there were another URL to support in this endpoint we could (a) declare another "def process2: Route" and then compose the two with "process ~ process2" using another somewhat cryptic Akka directive
On the client side there are a few more details to mention. My first reaction was that Akka HTTP client code looks somehow less straitforward than what one would see with, say, commons-http. Any HTTP client would work for this example but I wanted to see one with akka-http to better grok the API.
  • JSON marshalling with Spray was a puzzle again. It was not clear to me from the official documentation but googling helped.
  • Somewhat unusually, the client requires the same ActorSystem and Materializer infrastructure as the server. 
My conclusion is that if circumstances force me to implement anything RESTful I will reach for Akka HTTP first. But in comparison with real RPC it's more work to produce less safe code. But that's a topic for another day.