gRPC Remote Procedure Call (with Protobuf) – Grape Up

0

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 name
  • rpc Insert(Geolocation) – methods are defined by rpc keyword, its name, and request parameter type
  • returns (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 structure
  • message SearchResponse repeated Geolocation geolocations = 1; – if you want to return a list of objects, you have to mark them as repeated 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 send
  • responseObserver.onNext() – writes responses to the client. Unary calls must invoke onNext at most once
  • responseObserver.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

Leave a Reply