gRPC Remote Procedure Call (with Protobuf) – Grape Up
One of the most crucial technical decisions during designing API is to choose the proper protocol for interchanging data. It is not an easy task. You have to answer at least a few important questions – which will integrate with API, if you have any network limitations, what is the amount and frequency of calls, and will the level of your organization’s technological maturity allow you to maintain this in the future?
When you gather all the information, you can compare different technologies to choose one that fits you best. You can pick and choose between well-known SOAP, REST, or GraphQL. But in this article, we would like to introduce quite a new player in the microservices world – gRPC Remote Procedure Call.
What is gRPC (Remote Procedure Call)?
gRPC is a cross-platform open-source Remote Procedure Call (RPC) framework initially created by Google. The platform uses Protocol Buffers as a data serialization protocol, as the binary format requires fewer resources and messages are smaller. Also, a contract between the client and server is defined in proto
format, so code can be automatically generated. The framework relies on HTTP/2 (supports TLS) and beyond performance, interoperability, and code generation offers streaming features and channels.
Declaring methods in contract
Have you read our article about serializing data with Protocol Buffers? We are going to add some more definitions there:
message SearchRequest
string vin = 1;
google.protobuf.Timestamp from = 2;
google.protobuf.Timestamp to = 3;
message SearchResponse
repeated Geolocation geolocations = 1;
service GeolocationServer
rpc Insert(Geolocation) returns (google.protobuf.Empty);
rpc Search(SearchRequest) returns (SearchResponse);
The structure of the file is pretty straightforward – but there are a few things worth noticing:
service GeolocationServer
– service is declared by keyword with that namerpc Insert(Geolocation)
– methods are defined byrpc
keyword, its name, and request parameter typereturns (google.protobuf.Empty)
– and at the end finally a return type. As you can see you have to always return any value, in this case, is a wrapper for an empty structuremessage SearchResponse repeated Geolocation geolocations = 1;
– if you want to return a list of objects, you have to mark them asrepeated
and provide a name for the field
Build configuration
We can combine features of Spring Boot and simplify the setup of gRPC server by using the dedicated library GitHub – yidongnan/grpc-spring-boot-starter: Spring Boot starter module for gRPC framework. (follow the installation guide there).
It let us use all the goodness of the Spring framework (such as Dependency Injection or Annotations).
Now you are ready to generate Java code! ./gradlew generateProto
Server implementation
To implement the server for our methods definition, first of all, we have to extend the proper abstract class, which had been generated in the previous step:
public class GeolocationServer extends GeolocationServerGrpc.GeolocationServerImplBase
As the next step add the @GrpcService
annotation at the class level to register gRPC server and override server methods:
@Override
public void insert(Geolocation request, StreamObserver<Empty> responseObserver)
GeolocationEvent geolocationEvent = convertToGeolocationEvent(request);
geolocationRepository.save(geolocationEvent);
responseObserver.onNext(Empty.newBuilder().build());
responseObserver.onCompleted();
@Override
public void search(SearchRequest request, StreamObserver<SearchResponse> responseObserver)
List<GeolocationEvent> geolocationEvents = geolocationRepository.searchByVinAndOccurredOnFromTo(
request.getVin(),
convertTimestampToInstant(request.getFrom()),
convertTimestampToInstant(request.getTo())
);
List<Geolocation> geolocations = geolocationEvents.stream().map(this::convertToGeolocation).toList();
responseObserver.onNext(SearchResponse.newBuilder()
.addAllGeolocations(geolocations)
.build()
);
responseObserver.onCompleted();
StreamObserver<> responseObserver
– stream of messages to sendresponseObserver.onNext()
– writes responses to the client. Unary calls must invoke onNext at most onceresponseObserver.onCompleted()
– receives a notification of successful stream completion
We have to convert internal gRPC objects to our domain entities:
private GeolocationEvent convertToGeolocationEvent(Geolocation request)
Instant occurredOn = convertTimestampToInstant(request.getOccurredOn());
return new GeolocationEvent(
request.getVin(),
occurredOn,
request.getSpeed().getValue(),
new Coordinates(request.getCoordinates().getLatitude(), request.getCoordinates().getLongitude())
);
private Instant convertTimestampToInstant(Timestamp timestamp)
return Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanos());
Error handling
Neither client always sends us a valid message nor our system is resilient enough to handle all errors, so we have to provide ways to handle exceptions.
If an error occurs, gRPC returns one of its error status codes instead, with an optional description.
We can handle it with ease in a Spring’s way, using annotations already available in the library:
@GrpcAdvice
public class GrpcExceptionAdvice
@GrpcExceptionHandler
public Status handleInvalidArgument(IllegalArgumentException e)
return Status.INVALID_ARGUMENT.withDescription(e.getMessage()).withCause(e);
@GrpcAdvice
– marks the class as a container for specific exception handlers@GrpcExceptionHandler
– method to be invoked when an exception specified as an argument is thrown
Now we ensured that our error messages are clear and meaningful for clients.
gRPC – is that the right option for you?
As demonstrated in this article, gRPC integrates well with Spring Boot, so if you’re familiar with it, the learning curve is smooth.
gRPC is a worthy option to consider when you’re working with low latency, highly scalable, distributed systems. It provides an accurate, efficient, and language-independent protocol.
Check out the official documentation for more knowledge! gRPC