Building HTTP/2 services with gRPC
The basic idea behind gRPC is quite similar to that used by the RESTful services that make up so many web applications today. The methods of the server application are accessible to clients through a well-defined set of HTTP requests. The server answers each of these calls with an HTTP response (or, if necessary, an error code). But gRPC dispenses with HTTP 1.1, relying strictly on HTTP/2—and thus, the project claims, gaining considerable performance.
gRPC was announced in a February 26 blog
post. The post described gRPC as enabling "highly
performant, scalable APIs and microservices
" and said that
Google is starting to expose gRPC endpoints to its own services. It
also said that other companies have been involved in the creation of
gRPC, naming mobile-payment processor Square as one example. Square
posted an
account of its own that explains some of the background. Both of
the posts highlight a few key features that are enabled by
building on top of HTTP/2—namely, bidirectional streaming,
multiplexed connections, flow control, and HTTP header compression.
Those improvements, of course, would be available to any RPC framework using HTTP/2. As for gRPC itself, it is built on top of Google's protocol buffers, which was updated to version 3.0 (a.k.a. "proto3") in conjunction with the gRPC release. Proto3 is largely a stripped-down version of proto2: multiple fields have been removed, including several that were required in proto2 or that had required default values, and the extension mechanism has been replaced with a simple Any standard type that can hold. Proto3 also adds standard types for times and dates and for dynamic data, plus bindings for Ruby and JavaNano (an Android-centric Java library optimized for low-resource systems).
gRPC uses protocol buffers to serialize and deserialize its over-the-wire messages. Employing that technique leverages HTTP/2's binary wire format, allowing for more efficient RPC traffic than HTTP 1.1's ASCII encoding. But gRPC also uses proto3's interface definition language (IDL) to specify the structure and the content that a web service's RPC messages will take.
To get started, the application developer writes a schema for the new web service, defining each of the requests and the possible responses that the service will use. The protocol buffers compiler (protoc) can then be used to generate classes and stub code (for client and server) from the schema, in any of the supported languages. At launch time, the available languages were Python, C++, C#, Objective-C, PHP, Ruby, Go, Java (both in generic and Android flavors), and JavaScript tailored to use with Node.js.
Quick-start guides are available for most of the languages (C#, Objective-C, and PHP guides are absent at the moment). Each of these tutorials begins with the same basic service definition:
syntax = "proto2";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
One side point is worth noting: for reasons that are not explained, the project's Python tutorial does, indeed, specify proto2 syntax rather than proto3. For the simple "Hello World" example used, proto2 and proto3 syntax are identical (which is visible when comparing it with the Java and Go tutorials), but the generated code does leave some artifacts behind, in the _pb2 portion of the module name.
The output language of protoc compiler is specified with a command-line switch. Using --python_out, for example, generates a helloworld_pb2 module, a stub function called SayHello() for the client, and a Greeter class for the server that includes a stub function to respond to the SayHello() message as well as a simple serve() function that waits for incoming messages.
By using an abstract IDL, the argument goes, developers can quickly generate client code for a variety of different platforms, both desktop- and mobile-oriented. gRPC also makes it easy to include a version number in each message-format definition, so that clients and servers can support interoperability across versions.
The project has a detailed description of how gRPC's messages are sent in HTTP/2 frames. This is where some of HTTP/2's other advantages over HTTP 1.1 come into play.
For example, whenever an endpoint (client or server) sends a message, it can choose to keep the stream open, so that it can be used for bi-directional communication for the remainder of the session. gRPC uses HTTP/2's stream IDs as its internal identifiers for RPC calls, so the mapping between the RPC call and its associated stream comes for free. Multiplexing several open streams over one connection is another way that HTTP/2 enables more efficient use of the available bandwidth; it is up to the developer to determine how many active streams make sense for the service.
gRPC also piggybacks on HTTP/2 for its error codes. That allows the application to pick up on HTTP/2 error conditions (such as one endpoint of the stream throttling the connection) with little additional effort. Finally, gRPC is designed to support pluggable authentication methods; TLS 1.2 (or greater) and OAuth 2.0 are supported so far.
Where gRPC heads from here, naturally, remains to be seen. It is certainly a straightforward enough framework to warrant closer examination, and there will, no doubt, be many developers interested in seeing how they can take advantage of HTTP/2's promised improvements over HTTP 1.1.
That said, HTTP/2 is still in its
infancy—one should expect to see a wide array of new frameworks
announced in the next few years, all claiming to leverage the new
protocol for a range of enhanced features. gRPC may be one of the
first, but only time will tell how many developers outside of Google
and its partners find it a good fit.
