gRPC Unary API

Overview:

In this tutorial, I would like to show you how to implement a gRPC Unary API in Java.

I assume that you have a basic understanding of what gRPC is. If not, read the below articles first.

  1. Protocol Buffers – A Simple Introduction
  2. gRPC – An Introduction Guide

gRPC Unary API:

Unary is a simple request and response model. A client sends the request to the server and the server will process the request and respond back. This call can either be a blocking synchronous call or a non-blocking asynchronous call. Let’s take a look both options.

grpc unary api

Sample Application:

We need to implement a calculator application in which our back-end server has to find the factorial for the given number. To keep things simple, we will not be doing any error handling for now. It will be a separate article to explain in detail.

Protobuf – Service Definition:

Now we know what the business requirement is & what the client expects! So let’s create a service definition for this. findFactorial is going to be the method to be implemented on the server side. This service definition shows what type of input to be sent and what type of output to expect. By default if we do not use any stream keyword in the rpc, it is Unary.

syntax = "proto3";

package calculator;

option java_package = "com.vinsguru.calculator";
option java_multiple_files = true;

message Input {
  int32 number = 1;
}

message Output {
  int64 result = 1;
}

service CalculatorService {
  // unary
  rpc findFactorial(Input) returns (Output) {};
}

When we issue the below maven command, maven automatically creates the client and server side code using protoc tool.

mvn clean compile

For example, CalculatorServiceImplBase class in the below picture is auto-generated abstract class which needs to be implemented by the server for the above service definition. Similarly CalculatorServiceStub is the actual implementation class which client should use to make a request.

A simple service definition file does most of the heavy lifting already for the client server communication.

gRPC Unary API – Server Side:

Service Implementation: Let’s extend the abstract CalculatorServiceImplBase to add our implementation to respond to the findFactorial call. The implementation is very simple as shown here. The server will receive the Input type which we had defined using protobuf earlier.

  • Success response: We find the factorial for the given number and we respond back using responseObserver. We return the result Output object via onNext method to the calling client and we also notify the client that server job is done for this call by an onCompleted call.
  • Error response:  In case of any error in processing the request, the server will use onError to notify the client. We will discuss that in a separate article.
import com.vinsguru.calculator.CalculatorServiceGrpc;
import com.vinsguru.calculator.Input;
import com.vinsguru.calculator.Output;
import io.grpc.stub.StreamObserver;

public class UnaryCalculatorService extends CalculatorServiceGrpc.CalculatorServiceImplBase {
    
    @Override
    public void findFactorial(Input request, StreamObserver<Output> responseObserver) {
        int input = request.getNumber();
        long result = this.factorial(input);
        Output output = Output.newBuilder()
                .setResult(result)
                .build();
        responseObserver.onNext(output);
        responseObserver.onCompleted();
    }

    private long factorial(int number){
        if(number == 0)
            return 1;
        return number * factorial(number - 1);
    }
    
}

Once the service implementation is done, Let’s add it to the server to serve the client calls. We are listening on port 6565. Start this server by invoking the main method.

public class CalculatorServer {

    public static void main(String[] args) throws IOException, InterruptedException {

        // build gRPC server
        Server server = ServerBuilder.forPort(6565)
                .addService(new UnaryCalculatorService())
                .build();

        // start
        server.start();

        // shutdown hook
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("gRPC server is shutting down!");
            server.shutdown();
        }));

        server.awaitTermination();

    }

}

Now our server is ready, up and running!

gRPC Blocking Unary Call:

Protobuf already has generated the client library. The client has to do the following to make a request and receive the response.

  • Creating channel: The client has to create a channel/connection with the back-end server first.
  • Stub: The client will use either a blocking stub / non-blocking stub depends on its requirements to make a request by passing the required parameters.

To demo this first with blocking stub, I am going to create a simple JUnit test class to act like a gRPC client. Do note that client can be anything. It could even be another microservice.

public class UnaryServiceTest {

    private ManagedChannel channel;
    private CalculatorServiceGrpc.CalculatorServiceBlockingStub clientStub;

    @Before
    public void setup(){
        this.channel = ManagedChannelBuilder.forAddress("localhost", 6565)
                .usePlaintext()
                .build();
        this.clientStub = CalculatorServiceGrpc.newBlockingStub(channel);
    }

    @Test
    public void unaryServiceTest(){
        // build the request object
        Input input = Input.newBuilder()
                .setNumber(5)
                .build();

        // receive the response
        Output output = this.clientStub.findFactorial(input);

        //check the result
        Assert.assertEquals(120, output.getResult());
    }

    @After
    public void teardown(){
        this.channel.shutdown();
    }

}

When we run this, we are able to successfully verify that we get the results as expected.

gRPC Async Unary Call:

If the request is time consuming, we can make this request to be completely non-blocking by using asynchronous stub as shown below.

  • As a first step, we need to have an implementation for the StreamObserver.
public class OutputStreamObserver implements StreamObserver<Output> {

    @Override
    public void onNext(Output output) {
        System.out.println(
                "Received : " + output.getResult()
        );
    }

    @Override
    public void onError(Throwable throwable) {

    }

    @Override
    public void onCompleted() {

    }

}
  • Then we create an asynchronous stub and pass the above implementation.
public class UnaryServiceTest {

    private ManagedChannel channel;
    private CalculatorServiceGrpc.CalculatorServiceStub clientStub;

    @Before
    public void setup(){
        this.channel = ManagedChannelBuilder.forAddress("localhost", 6565)
                .usePlaintext()
                .build();
        this.clientStub = CalculatorServiceGrpc.newStub(channel);
    }

    @Test
    public void unaryServiceTest(){
        // build the request object
        Input input = Input.newBuilder()
                .setNumber(5)
                .build();

        // receive the response
        this.clientStub.findFactorial(input, new OutputStreamObserver());
    }

    @After
    public void teardown(){
        this.channel.shutdown();
    }

}

Now if we execute the client side code, the client does not wait for the response to come back. The StreamObserver implementation gets executed asynchronously when the response is received.

BloomRPC:

It is very very easy to test REST based APIs by using postman / web browsers. But testing gRPC might not look easy so far. It seems to require writing separate code. That is where BloomRPC comes into picture which provides the postman like experience. Download and install from the release page.

  • Once installed, launch BloomRPC and load the proto file
  • BloomRPC automatically shows all the method calls
  • Update the server port and ip
  • Send the request and get the response


gRPC Course:

I learnt gRPC + Protobuf in a hard way. But you can learn them quickly on Udemy. Yes, I have created a separate step by step course on Protobuf + gRPC along with Spring Boot integration for the next generation Microservice development. Click here for the special link.


Summary:

We were able to successfully implement a gRPC server and invoke the unary API using both synchronous and asynchronous way . Lets explore streaming APIs in this article.

The source code is available here.

Happy learning 🙂

Share This:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.