gRPC

ServiceTalk gRPC support is an active work-in-progress and is not yet peformance tested.

Motivation

A design philosophy for ServiceTalk is cross protocol API symmetry which means that all protocols supported by ServiceTalk should have same constructs and follow the same design principles. We acknowledge that grpc-java implements the gRPC wire protocol for the JVM and is used extensively. However, our design philosophies are different than grpc-java and ServiceTalk also provides HTTP/2 directly for users. The grpc-java team and the ServiceTalk team collaborated to bring HTTP/2 support to Netty, and both share the same underlying protocol implementation. Once you have HTTP/2 support the investment to support the gRPC wire protocol is relatively small but it enables ServiceTalk users to benefit from our design philosophy consistently across all protocols.

Overview

gRPC support in ServiceTalk implements the gRPC wire protocol and provides ServiceTalk APIs for that protocol. It provides all the different Programming Paradigms for client and server. Here is a quick start example of the blocking and aggregated paradigm:

Blocking Client

try (BlockingGreeterClient client = GrpcClients.forAddress("localhost", 8080)
        .buildBlocking(new ClientFactory())) {
    HelloReply reply = client.sayHello(HelloRequest.newBuilder().setName("Foo").build());
    // use the response
}

Blocking Server

GrpcServers.forPort(8080)
        .listenAndAwait(new ServiceFactory((BlockingGreeterService) (ctx, request) ->
                HelloReply.newBuilder().setMessage("Hello " + request.getName()).build()))
        .awaitShutdown();

Extensibility and Filters

The design of this protocol involves configuring builders for core protocol concerns, and then appending Filters for extensibility. Filters are described in more detail below but in general they facilitate user code to filter/intercept/modify the request/response processing. Filters can be used for cross-cutting concerns such as authentication, authorization, logging, metrics, tracing, etc…​

Server

The server side is built around the concept of Service. A Service is where your business logic lives. Interface for user service is generated from a provided protocol buffers service definition. Users implement this interface and provide it to the ServiceTalk gRPC server. ServiceTalk internally uses the existing HTTP module as the transport for gRPC. The flow of data from the socket to the gRPC Service is visualized as follows:

+--------+ request  +---------+       +----------+ request  +---------+       +----------+
|        |--------->|  HTTP   |------>|  HTTP    |--------->|  gRPC   |------>|  gRPC    |
| Socket |          | Decoder |       | Service  |          | Decoder |       | Service  |
|        |<---------| Encoder |<------|(for gRPC)|<---------| Encoder |<------|          |
+--------+ response +---------+       +----------+ response +---------+       +----------+

Each Service has access to a GrpcServiceContext which provides additional context (via ConnectionContext) into the Connection/transport details for each request/response. This means that a GrpcService method may be invoked for multiple connections, from different threads, and even concurrently.

HTTP Filters

As gRPC module is built on top of HTTP module, one can use the HTTP service filters if required to intercept the HTTP layer.

gRPC Filters

In addition to HTTP filters, gRPC users can also add gRPC filters which follow the same interface definition as the service and can be composed using the generated GrpcServiceFactory for a particular service definition.

Client

A Client is created via the GrpcClients static factory. It manages multiple Connections via a LoadBalancer. The control flow of a request/response can be visualized in the below diagram:

                                                                                   +--------------+     +----------------------+     +--------+
                                                                              /--->| Connection 1 |<--->| HTTP Decoder/Encoder |<--->| Socket |
                                                                              |    +--------------+     +----------------------+     +--------+
+--------+ request  +---------+       +--------+ request  +--------------+    |
|  gRPC  |--------->|  gRPC   |------>|  HTTP  |--------->|              |    |    +--------------+     +----------------------+     +--------+
| Client |          | Decoder |       | Client |          | LoadBalancer |<---+--->| Connection 2 |<--->| HTTP Decoder/Encoder |<--->| Socket |
|        |<---------| Encoder |<------|        |<---------|              |    |    +--------------+     +----------------------+     +--------+
+--------+ response +---------+       +--------+ response +--------------+    |
                                                                              |    +--------------+     +----------------------+     +--------+
                                                                              \--->| Connection x |<--->| HTTP Decoder/Encoder |<--->| Socket |
                                                                                   +--------------+     +----------------------+     +--------+

The LoadBalancer is consulted for each request to determine which connection should be used. The LoadBalancer interface is extensible and the reference implementation provides a Round Robin algorithm.

HTTP Filters

As gRPC module is built on top of HTTP module, one can use the HTTP client filters if required to intercept the HTTP layer.

gRPC Filters

In addition to HTTP filters, gRPC users can also add gRPC filters which follow the same interface definition as the service and can be composed using the generated GrpcClientFactory for a particular service definition.

Connection Filters

gRPC clients support adding connection filters similar to the HTTP module.

Service Discovery

gRPC clients uses Service discovery to discover instances of the target service similar to the HTTP module.