gRPC using Java

gRPC using Java
Photo by Domenico Loia / Unsplash
What is gRPC

As per the official site -

gRPC is a modern open-source high-performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking, and authentication. It is also applicable in the last mile of distributed computing to connect devices, mobile applications, and browsers to backend services.

Some salient features -

  • Simple service definition using Protocol Buffers
  • Easily scalable
  • Works across languages and platforms
  • Supports Bidirectional streaming with HTTP/2-based transport

gRPC Architecture

gRPC is based on the idea of defining a service, specifying the methods that can be called remotely with their parameters and return types. On the server side, the server implements this interface and runs a gRPC server to handle client requests. On the client side, the client has a stub that provides the same methods as the server.


Protocol Buffers

Protobuf is the most commonly used IDL (Interface Definition Language) for gRPC. It's where you store your data and function contracts in the form of a proto file.

Both Client and Server need to have the same proto file which acts as a contract between them.

Protobuf is popular since it sends over JSON strings as bytes, making it much smaller and enabling faster performance.

Learn more about it here.


Now that we know some theory, let's get our hands dirty! We will use Java to create a gRPC server and call it using postman.

Code Setup
  • Let's create a new maven project in Inteiij/Eclipse
  • Now let's add a few required dependencies

See the detailed pom file here

  • Now we will create the proto file as below -
syntax = "proto3";

option java_package = "in.bitmaskers.grpc";
option java_outer_classname = "Todos";

service TodoStore {
  rpc add (Todo) returns (APIResponse);
  rpc searchTodo(TodoSearch) returns (Todo);
  rpc listTodos(Empty) returns (stream Todo);
}

message Todo {
  string name = 1;
}

message TodoSearch {
  string name = 1;
}

message Empty {
}

message APIResponse{
  string responseMessage = 1;
  int32 responseCode = 2;
}
  • Now let's run 'mvn clean install' from the location of the app and we can see the appropriate java files will be created
  • Now we will create TodosStoreService which will extend TodoStoreGrpc.TodoStoreImplBase and override the methods.

Creating the Server

Now let's implement the methods created in the previous steps

  • First, we create a transient memory Todo List to save the todos. We can always hook it to a database but let's keep it in memory for this article
  • Method to add Todos
@Override
    public void add(Todos.Todo request, StreamObserver<Todos.APIResponse> responseObserver) {
        String name = request.getName();
        Todos.Todo todo = Todos.Todo.newBuilder().setName(name).buildPartial();
        todoList.add(todo);
        Todos.APIResponse.Builder apiResponse = Todos.APIResponse.newBuilder();
        apiResponse.setResponseCode(0);
        apiResponse.setResponseMessage("Todo added successfully");
        responseObserver.onNext(apiResponse.build());
        responseObserver.onCompleted();
    }

Here, we take the input name from the request, create a new Todo using the Builder pattern, and then add it to in memory TodoList.

Additionally, we also create an apiResponse which is sent back using responseObserver.

  • Method to searchTodo
@Override
    public void searchTodo(Todos.TodoSearch request, StreamObserver<Todos.Todo> responseObserver) {
        String name = request.getName();
        Optional<Todos.Todo> optionalTodo = todoList.stream().filter(todo -> todo.getName().equals(name)).findFirst();
        if (optionalTodo.isPresent()) {
            responseObserver.onNext(optionalTodo.get());
        } else {
            responseObserver.onNext(Todos.Todo.newBuilder().buildPartial());
        }
        responseObserver.onCompleted();
    }

Here we use streams API to search for the todo with the name passed and return it if found else returns empty

  • Method to  stream a list of Todos

This is an interesting one. We are not sending the entire Todo List in one go but streaming it using Server Streaming capability.

@Override
    public void listTodos(Todos.Empty request, StreamObserver<Todos.Todo> responseObserver) {
        for (Todos.Todo todo : todoList) {
            responseObserver.onNext(todo);
            try {
                // Replicate time-consuming IO Calls
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        responseObserver.onCompleted();
    }

Here we are using Thread sleep of 5000ms to replicate some time-consuming IO calls.

  • The Server

Easiest one of all. We create a main method that can be used to run the code.

public class GRPCServer {
    public static void main(String[] args) throws IOException, InterruptedException {
        Server server = ServerBuilder.forPort(9090).addService(new TodosStoreService()).build();
        server.start();
        System.out.println("Server started at " + server.getPort());
        server.awaitTermination();
    }
}

All right our server is ready. Now let's run it and use Postman to perform some calls.

Setting postman
  • Create a new gRPC request
  • Import the proto file created before

Import is as an API

  • We should now see the URL and methods appearing

Let's now run all the methods and see them in action

  • Add Todo
  • Search Todo
  • List Todos

See the response is a streaming one with some gap in between!


All right folks! This was a long one but enjoyed learning something new.

Hope you like it. Cheers 🍻