The founder of Netty introduces a new microservice framework ‘Armeria’. It is unique because it 1) has Netty-based high-perf HTTP/2 implementation, 2) lets you run gRPC, Thrift, REST, even Servlet webapp on single TCP port in single JVM, and 3) integrates with Spring Webflux and Reactive Streams.
Armeria is a Netty-based open-source Java microservice framework which provides an HTTP/2 client and server implementation. It is different from any other RPC frameworks in that it supports both gRPC and Thrift. It also supports RESTful services based on Reactive Streams API and even a legacy web applications that run on Tomcat or Jetty, allowing you to mix and match different technologies into a service which means you do not need to launch multiple JVMs or open multiple TCP/IP ports just because you have to support multiple protocols or migrate from one to another.
In this session, Trustin Lee, the founder of Netty project and Armeria, shows:
What Armeria is.
How to serve gRPC, Thrift and RESTful services on a single TCP/IP port and a single JVM.
How to make your legacy Tomcat or Jetty-based application and modern reactive RPC service coexist.
How to use Armeria’s universal decorator API to apply common functionalities such as circuit breaker, DNS-based service discovery, distributed tracing and automatic retry, regardless of the protocol, which was previously impossible with other RPC frameworks which focused on a single protocol.
7. You can’t solve this with:
●
More CPUs
– They are very idle.
●
More threads
– They will all get stuck with Shard 2 in no time.
– Waste of CPU cycles – context switches are not free.
– Waste of memory – each thread needs its own stack.
●
Result:
– Fragile system that falls apart even on a tiny backend failure
– Inefficient system that takes more memory and CPU than what it actually needs
●
Maybe you can work around this, but you’ll have to keep tuning and adding hacks.
8. What is Armeria?
A user-friendly Java microservice framework
based on asynchronous and reactive model
with HTTP/2 and modern RPC protocols in mind
9. Problems with large payloads
●
We solved blocking problem with asynchronous programming,
but can we send 10MB personalized response to 100K clients?
– Can’t hold that much in RAM – 10MB × 100K = 1TB
●
What if we/they send too fast?
– Different bandwidth & processing power
●
We need ‘just enough buffering.’
– Expect OutOfMemoryError otherwise.
10. What is Armeria?
A user-friendly Java microservice framework
based on asynchronous and reactive model
with HTTP/2 and modern RPC protocols in mind
11. RPC vs. HTTP impedance mismatch
●
RPC has been hardly a 1st-class citizen in web frameworks.
– Method name
– Parameter names and values
– Return value or exception
POST /some_service HTTP/1.1
Host: example.com
Content-Length: 96
<binary request>
HTTP/1.1 200 OK
Host: example.com
Content-Length: 192
<binary response>
Failed RPC call
12. Access logs re-imagined
●
What if we provide access logs with …
– Universal API for gRPC · Thrift · REST
– RPC-level information
– Precise send · receive timings
●
Imagine streaming to data processing pipeline, e.g. Kafka.
192.167.1.2 - - [10/Oct/2000:13:55:36 -0700]
"POST /some_service HTTP/1.1" 200 2326
13. Making a debug call
●
Sending an ad-hoc query in RPC is hard.
– Find a proper service definition, e.g. .thrift or .proto files
– Set up code generator, build, IDE, etc.
– Write some code that makes an RPC call.
●
HTTP in contrast:
– cURL, telnet command, web-based tools and more.
●
There’s some effort to make things easier.
– gRPC reflection service + grpcurl
●
What if we build something more convenient and collaborative?
14. More than one RPC protocol
●
Supports both gRPC & Thrift …
– on a single HTTP port
– in a single JVM
– without any proxies?
●
Can serve non-RPC requests together.
– REST API – Static files
– Exposing metrics – Health-check requests from load balancers
– Traditional JEE webapps
●
Share common logic between different endpoints!
15. What is Armeria?
A user-friendly Java microservice framework
based on asynchronous and reactive model
with HTTP/2 and modern RPC protocols in mind
16. Better UX for devs
●
The last yet most important
●
Nobody wants a framework that’s hard to use.
●
Can we design …
– a user-friendly asynchronous API
– … with modest learning curve?
17. Our answer – Armeria
●
Asynchronous + High-performance
– Netty with JNI-based /dev/epoll transport and BoringSSL
●
Seamless integration with Reactive Streams
– RxJava2, Project Reactor, Spring WebFlux…
●
Ability to mix different types of services in a single server
– gRPC, Thrift, REST, Tomcat, Jetty…
●
Not just HTTP/2
– gRPC on HTTP/1
– HAProxy
●
Easy to learn and fun to use (hopefully)
19. Hello, world!
Server server = new ServerBuilder()
.http(8080)
.https(8443)
.tlsSelfSigned()
.haproxy(8080)
.service("/", (ctx, req) -> HttpResponse.of("Hello, world!"))
.build();
server.start();
Protocol auto-detection at 8080
20. Hello, world – Parameterized
Server server = new ServerBuilder()
.http(8080)
.service("/hello/:name",
(ctx, req) -> HttpResponse.of("Hello, %s!",
ctx.pathParam("name")))
.build();
server.start();
21. Hello, world – Annotated
Server server = new ServerBuilder()
.http(8080)
.annotatedService(new Object() {
@Get("/hello/:name")
public String hello(@Param String name) {
return String.format("Hello, %s!", name);
}
})
.build();
server.start();
22. There are more!
●
CompletableFuture and reactive responses
●
Query parameters and cookies
●
Request and response converters
●
Exception handlers
●
Content type negotiation
●
Regular-expression path mapping
●
Aims to be on-par or better than <your favorite web framework>
24. gRPC & Thrift basics
a. Write an IDL file – .proto or .thrift
b. A compiler compiles it into .java files.
c. Implement interfaces generated at ‘b’.
namespace java com.example
service HelloService {
string hello(1:string name)
}
syntax = "proto3";
package grpc.hello;
option java_package = "com.example";
service HelloService {
rpc Hello (HelloRequest)
returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
25. gRPC
Server server = new ServerBuilder()
.http(8080)
.service(new GrpcServiceBuilder().addService(new MyHelloService())
.build())
.build();
class MyHelloService extends HelloServiceGrpc.HelloServiceImplBase {
@Override
public void hello(HelloRequest req, StreamObserver<HelloReply> cb) {
HelloReply reply = HelloReply.newBuilder()
.setMessage("Hello, " + req.getName() + '!')
.build();
cb.onNext(reply);
cb.onCompleted();
}
}
26. Thrift
Server server = new ServerBuilder()
.http(8080)
.service("/hello", THttpService.of(new MyHelloService()))
.build();
class MyHelloService implements HelloService.AsyncIface {
@Override
public void hello(String name, AsyncMethodCallback<String> cb) {
cb.onComplete("Hello, " + name + '!');
}
}
27. What’s better than the official impls?
●
gRPC
– Works on both HTTP/1 and 2
– gRPC-Web support, i.e. can call gRPC services from JavaScript frontends
●
Thrift
– HTTP/2
– TTEXT (human-readable REST-ish JSON)
●
Can mix gRPC, Thrift, REST, Tomcat, Jetty, …
●
Can leverage the common Armeria features
– Decorators
– Web-based dashboard
34. Armeria documentation service
●
Enabled by adding DocService to ServerBuilder
●
Browse and invoke RPC services in an Armeria server
– No fiddling with binary payloads
– Send a request without writing code
●
Supports gRPC, Thrift and annotated services
●
We have a plan to add:
– Metric monitoring console
– Runtime configuration editor, e.g. logger level
40. Armeria client API
●
Similar experience to the server-side API
– e.g. Common types, Decorators, RPC-friendliness
●
Client-specific stuff
– Circuit breakers
– Automatic retries
– Client-side load balancing
– Retrofit integration – https://square.github.io/retrofit/
41. // Send DNS queries periodically to discover service hosts (k8s style).
DnsServiceEndpointGroup group =
DnsServiceEndpointGroup.of("my-service.cluster.local");
// Register the group into the registry.
EndpointGroupRegistry.register("myService", group,
EndpointSelectionStrategy.WEIGHTED_ROUND_ROBIN);
// Create a new HTTP client that connects to the group
// with retries, circuit breaker and logging.
HttpClient client = new HttpClientBuilder("http://group:myService")
.decorator(RetryingHttpClient.newDecorator(onServerErrorStatus()))
.decorator(CircuitBreakerHttpClient.newDecorator(...))
.decorator(LoggingClient.newDecorator())
.build();
// Send a request and print the response content.
client.get("/greet").aggregate()
.thenAccept(res -> System.out.println(res.contentUtf8()))
.exceptionally(cause -> { /* Handle an error. */ });
43. Let’s build Armeria together!
●
Give it a try.
●
Ask questions.
●
Request new features.
●
Tell us what rocks and sucks.
●
Consider joining the effort.
44. Meet us at GitHub and Slack
●
https://github.com/line/armeria
●
https://line-slacknow.herokuapp.com/line-armeria/