Earlier this year, gRPC became part of the Cloud Native Computing Foundation (CNCF) where it joined other open source technologies that are designed for creating modern distributed systems. It is a lightweight and efficient Remote Procedure Call (RPC) framework designed to create high-performance and cross-platform machine-to-machine services. gRPC originated from work that started at Google in collaboration with Square, Inc. Besides Google and Square, many other companies have adopted gRPC including CoreOS, Netflix, Cisco, and others.

At {code}, gRPC has become a critical technology for some projects. As an early implementer of the Container Storage Interfaces (CSI) and a member of the Cloud Native Computing Foundation, we are using gRPC to create tools to bring universal, seamless, and vendor-agnostic storage for containerized workload running on schedulers like Mesos and Kubernetes. This post explains how gRPC works with a simple example.

An Overview

gRPC is the continuation of the RPC concept which allows one process to seamlessly invoke methods running on a remote machine.

gRPC borrows and improves on ideas from many technologies before it:

  • Like CORBA, gRPC uses an Interface Definition Language (IDL) to define service contracts.
  • Like Java RMI, gRPC clients invoke remote methods as if they were running locally.
  • Like SOAP, gRPC tools can auto-generate client and server codes for several languages.
  • Like REST, gRPC uses HTTP (specifically HTTP/2) as transport mechanism.

However, gRPC is not just a set of loosely-related technologies, but a framework intentionally designed for the creation of service architectures ranging from datacenter scale computing to small IoT devices. gRPC:

  • Uses Protocol Buffers as a typed and efficient binary format to keep payload light and fast (instead of text-based XML or JSON).
  • Uses HTTP/2 which multiplexes long-lived TCP connections into multiple channels for concurrent and efficient communication.
  • Supports bidirectional streaming between clients and servers.
  • Has an extensive middleware API to implement structural concerns such as authentication, authorization, flow-control, tracing, circuit-breaking, and service policies.

The gRPC framework is also cross-platform. The project prides itself on supporting 10 languages (see below) that are maintained with comparable capabilities.

This means a service can be written in Java and the gRPC tools can generate client stubs to access that service in any of the languages listed above.

How it works

In most languages, creating a gRPC service can be distilled to three basic steps:

First you define the protocol buffers IDL file containing the service contracts. Next, use the protocol buffers compiler protoc to generate server interfaces and client stubs targeting a language of your choice. Lastly, you implement the methods for the service interfaces as they are defined in your IDL.

Creating a Greeter Service

So let us use the steps listed above to create a simple Greeter service. As mentioned, the first step is to define the service using a protocol buffers IDL file. In it, we declare the service name, its methods, and the parameters for the methods as shown in the figure below:

greeter.proto

The service above exposes one method, Greet(), which takes the parameter GreetRequest and returns parameter GreetResponse. When the IDL is compiled, the compiler will generate types that are native to the targeted language. For our example, we decide to write the server using the Go programming language. So, we use the protocol buffers compiler to generate source code for the service interface as follows:

$> protoc –go_out=plugins=grpc:. greeter.proto

The previous command will generate a Go source file called greeter.bp.go (see below). The file contains an interface, and other supporting types and function, necessary to create our gRPC server:

greeter.pb.go

To create the gRPC service, we need to write code that implements the interface above by specifying the actual behavior we want when the client calls method Greet. For our simple server implementation, method Greet() returns a greeting message back to the client as shown:

greetsvr.go

Once we have a working server, we can use the generated client stubs from any of the supported languages to invoke the service. For instance using the following command, we can create client stubs in Python:

$> python -m grpc.tools.protoc \ –python_out=. –grpc_python_out=. \ –proto_path=protobuf greeter.proto

Next, we use the generated stubs to create a simple Python client to invoke our remote method as shown in the snippet below. Notice in the Python code, method stub.Greet() is invoked as if it were a local call. In reality, the gRPC framework hides all of the complexities necessary to serialize and deserialize protocol buffers payload to and from the remote server.

greet_client.py

When the Python client is executed, it prints the message from the server as expected:

$> python greet_client.py Hello,Vladimir

Conclusion

If you are developing service architectures (micro or otherwise), you owe it to yourself to start looking at gRPC. gRPC has strong support for a relatively large number of languages covering a wide range of platforms from mobile to backends. The framework uses protocol buffers and HTTP/2 to create efficient services with support for features not found in JSON-based RESTful platforms. We at {code} have been putting gRPC to work with our latest version of REX-Ray with CSI Enablement. Make sure to catch my talk at Open Source Summit at 4:00pm Monday titled gRPC and Go: Developing Efficient and Type-Safe Services.

References