gRPC is an open source remote procedure call (RPC) framework. It was released by Google in 2015 and is now an incubating project within the Cloud Native Computing Foundation. This post introduces gRPC while explaining its underlying architecture and how it compares to REST over HTTP. You'll also get started using Quarkus to implement and consume gRPC services.
Remote method calling in gRPC
Wait, what’s this? Did you say remote method calling? Isn’t that something we did in the ‘90s with things like CORBA, RMI, and XML-RPC/SOAP?
Well, from a conceptual standpoint, yes. The question is, "How do those older technologies relate to a modern framework like gRPC?"
gRPC is similar to CORBA and RMI in that they require generating and using client and server bindings. This, however, is really where the similarities end. The underlying transport mechanisms used and the tooling available for use in today's technology frameworks are the main differences.
gRPC uses HTTP/2 for transport and protocol buffers as both its Interface Definition Language (IDL) and as its underlying message interchange format. In addition, tooling is built-into the framework for generating cross-platform client and server bindings for many languages and frameworks.
Comparing gRPC and REST
Ok, so you say gRPC uses HTTP/2 for transport? I’m already using REST over HTTP, so why should I care about gRPC?
Contract implementation is a critical difference between the two technologies. In REST, your contract (think OpenAPI) focuses more on the resources and uses standard practices around which HTTP "verbs" map to which actions on those resources. In most cases, REST uses JSON and XML as the format for the body of requests and responses. These formats are optimized to be human-readable, therefore require processing to be able to be read and produced by machines.
REST also does not offer guidance nor implementation details for implementing contracts. You aren’t required to generate any code to implement your contracts. There is also no standardization as to which tool to use if you do want to generate code that implements the client or server components of a contract.
In contrast to REST's focus on resources, gRPC instead focuses on defining services. These definitions specify the methods that can be called on that service with their parameters and return types. As a result, gRPC services are strongly-typed systems, meaning that the contracts on both ends of a system are well-defined. gRPC also includes client and server code generation tooling out of the box. This enables you, the developer, to focus on building the business logic on the server side and calling the service from the client. The implementation of the communication layer on both ends is generated for you.
Furthermore, the transport "across the wire" is optimized to be machine-readable. It nearly eliminates the transformation overhead required by JSON and XML. In addition, gRPC provides support for authentication, tracing, and health monitoring right out of the box! This makes gRPC a framework well-suited for low-latency, highly-scalable distributed systems. On the other hand, gRPC is not a great fit for systems that don’t support one of the binary protocols like protobuf.
In fact, many of the internal services and APIs within Kubernetes are implemented with gRPC!
Choosing the right framework
So where does that leave us? As with most technologies today, choosing one over another isn’t an either/or question. We need to choose the right tool to solve the right problem. Many organizations choose to use REST for public-facing applications and gRPC for internal service-to-service communication.
RESTful endpoints are rich with hypermedia and easy to self-discover. These characteristics make REST an ideal choice when you want to expose services for other organizations (or other business units within an organization) to consume. It's easy to proxy RESTful services and use API management to control access to them, enforcing policies like rate limiting, access control, etc.
But what about the web of downstream services that sit behind a public-facing service? Wouldn’t it make sense to have strongly-typed contracts with high-throughput/low-latency between them? gRPC can be as much as seven times faster than REST when receiving data and 10 times faster when sending data.
Using gRPC in Quarkus
Supersonic, Subatomic, Java to the rescue. Quarkus is an optimized Java stack crafted from best-of-breed libraries and standards and tailored for OpenJDK HotSpot and GraalVM. Support for gRPC was introduced to Quarkus in version 1.5. As you'll see, Quarkus makes it really easy to implement and consume gRPC services.
Implementing a gRPC service
After adding the Quarkus gRPC extension (mvn quarkus:add-extension -Dextensions=”grpc”
) you only need to define your protobuf files in your src/main/proto
directory. One such example (helloworld.proto
) might look like:
syntax = "proto3"; option java_multiple_files = true; option java_package = "io.quarkus.example"; option java_outer_classname = "HelloWorldProto"; package helloworld; // 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; }
When you run mvn compile
, the Quarkus maven plugin triggers the gRPC generator to generate the gRPC base implementation classes. Quarkus also generates a health check service for you so clients (like the Kubernetes scheduler for instance) can check whether or not your service is alive.
Next, you need to decide if you will implement a blocking service, a reactive service (using the Mutiny reactive library), or a service that returns streams of data. gRPC provides all of the necessary base implementation classes to extend and implement your business functionality. When running in Quarkus’ dev mode, the gRPC reflection service is automatically enabled. The reflection service makes it possible for tools like grpcurl or grpcox to interact with your service, or for other client services to call your service.
Consuming a gRPC service
Consuming a gRPC service is similar to implementing one. First, the gRPC extension needs to be added to your project. Second, you need to define your protobuf files. When compiling, the Quarkus maven plugin generates the client stubs in both blocking and reactive versions. You can then inject them into your application by using one of the following:
@Inject @GrpcService("hello-service") MutinyGreeterGrpc.MutinyGreeterStub mutinyHelloService; @Inject @GrpcService("hello-service") GreeterGrpc.GreeterBlockingStub blockingHelloService; @Inject @GrpcService("hello-service") Channel channel;
Finally, you can use the client stubs to make calls to the service. Here is how you would expose a REST API that calls a gRPC service:
@GET @Path("/blocking/{name}") public String helloBlocking(@PathParam("name") String name) { return this.blockingHelloService .sayHello( HelloRequest.newBuilder() .setName(name) .build() ).getMessage(); } @GET @Path("/mutiny/{name}") public Uni<String> helloMutiny(@PathParam("name") String name) { return this.mutinyHelloService .sayHello( HelloRequest.newBuilder() .setName(name) .build() ) .onItem() .transform(HelloReply::getMessage); }
Now that wasn’t too hard! Quarkus really does make this easy!
Wrap-up
If you want to see more of these examples, check out these short videos that showcase everything in action.
How to Implement a Blocking gRPC Service on Quarkus (Part 1)
How to Consume a Reactive gRPC Service on Quarkus Mutiny (Part 2)
I also recommend Quarkus "Q" Tip - Discover the Quarkus GRPC extension.
Want to learn more about Quarkus? Check out this paper to see how Quarkus can save as much as 64% of cloud resources as compared to other Java frameworks out there.
Commercial support for Quarkus is available as part of Red Hat Runtimes as well as part of a Red Hat OpenShift subscription.
References
Here are some references for further reading:
Last updated: December 21, 2020