Published on

gRPC in Microservices: Example in Golang and Java

Authors
grpc

What is gRPC?

gRPC is an open-source, high-performance remote procedure call (RPC) framework developed by Google. It is designed to enable client and server applications to communicate transparently and efficiently across different languages and platforms.

gRPC uses Protocol Buffers, a language-agnostic data serialization format, to define the interfaces and methods that are available on the server, which are then used to generate the client-side code. It supports many programming languages, including C++, Java, Python, Go, and many others.

More details: Unlocking the Benefits of gRPC for API Communication Beyond REST

Example

In this article, we’ll look at how to set up gRPC in Java and Golang and use it to communicate with each other. As an example, we will create two services: Profile (Java) and Department (Golang). The request flow will look like this:

grpc-golang-java-flow
  1. The user will make a request to the Profile service, by providing the user name in the request using the gRPC client (Postman).
  2. Under the hood, the Profile service will contact the Department service using gRPC to retrieve departmental information.
  3. After receiving the user’s department details from the Department service, the Profile service will construct a response and transmit it to the user.

Department Service (Java):

gRPC setup:

  1. Create a Maven Java project.
  2. Create a protobuf file inside the directory “src/main/proto” named “department. proto”, and paste the following code into it.
syntax = "proto3";

option java_multiple_files = true;
option go_package = "./proto";

package department;

message FetchDepartmentRequest {
  string name = 1;
}

message FetchDepartmentResponse {
  string name = 1;
}

service DepartmentService {
  rpc GetDepartmentByUserName(FetchDepartmentRequest) returns (FetchDepartmentResponse);
}
  1. Open the pom.xml file and add the following dependencies, extensions, and plugins respectively
<dependencies>
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-netty-shaded</artifactId>
        <version>1.40.1</version>
    </dependency>
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-protobuf</artifactId>
        <version>1.40.1</version>
    </dependency>
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-stub</artifactId>
        <version>1.40.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>annotations-api</artifactId>
        <version>6.0.53</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
<build>
  <extensions>
      <extension>
          <groupId>kr.motd.maven</groupId>
          <artifactId>os-maven-plugin</artifactId>
          <version>1.6.2</version>
      </extension>
  </extensions>
  <plugins>
      <plugin>
          <groupId>org.xolstice.maven.plugins</groupId>
          <artifactId>protobuf-maven-plugin</artifactId>
          <version>0.6.1</version>
          <configuration>
              <protocArtifact>com.google.protobuf:protoc:3.17.3:exe:${os.detected.classifier}</protocArtifact>
              <pluginId>grpc-java</pluginId>
              <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.40.1:exe:${os.detected.classifier}</pluginArtifact>
          </configuration>
          <executions>
              <execution>
                  <goals>
                      <goal>compile</goal>
                      <goal>compile-custom</goal>
                  </goals>
              </execution>
          </executions>
      </plugin>
  </plugins>
</build>
  1. Now run the command
mvn clean compile install

It will generate the Java code based on the “department.proto” file.

Implement Department Service:

  1. Create a class in the directory “src/main/java/services” and add following code:
package services;


import department.Department;
import department.DepartmentServiceGrpc;
import io.grpc.stub.StreamObserver;

import java.util.HashMap;
import java.util.Map;

// Extending gRPC generated code to override method and add custom implementation
public class DepartmentService extends DepartmentServiceGrpc.DepartmentServiceImplBase {
    private final Map<String, String> departmentsDetail = new HashMap<>();

    public DepartmentService() {
        departmentsDetail.put("John", "IT");
        departmentsDetail.put("Mark", "Teaching");
    }

    @Override
    public void getDepartmentByUserName(Department.FetchDepartmentRequest request, StreamObserver<Department.FetchDepartmentResponse> responseObserver) {
        String userName = request.getName();
        String name = "User is not assigned to any department";
        if (departmentsDetail.containsKey(userName)) {
            name = departmentsDetail.get(userName);
        }

        Department.FetchDepartmentResponse response = Department.FetchDepartmentResponse.newBuilder().setName(name).build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}
  1. Now we need to start our gRPC server, for that we will create a class named “GrpcServer” at “src/java” and add following code:
import io.grpc.Server;
import io.grpc.ServerBuilder;
import services.DepartmentService;

import java.util.HashMap;
import java.util.Map;

public class GrpcServer {
    private Server server;

    public static void main(String[] args) throws Exception {
        GrpcServer grpcServer = new GrpcServer();
        grpcServer.start();
        grpcServer.blockUntilShutdown();
    }

    private void start() throws Exception {
        int port = 9090;

        // Binding Our Implementations in our case department service
        server = ServerBuilder.forPort(port)
                .addService(new DepartmentService())
                .build()
                .start();

        System.out.println("Server started on port " + port);

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("Shutting down gRPC server...");
            GrpcServer.this.stop();
            System.out.println("gRPC server shut down.");
        }));
    }

    private void stop() {
        if (server != null) {
            server.shutdown();
        }
    }

    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }
}
  1. We are done now we can make a call using any gRPC client for testing we will use Postman:
grpc-postman-java

Now we will implement the Profile (Golang) service to make a grpc call to our Department (Java) service and fetch department details.

Profile Service (Golang):

grpc setup:

  1. Create a Golang project, and initialize it.
  2. Then we need to install some packages for gRPC using commands:
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
go get -u google.golang.org/grpc
  1. Now we need to create 2 probuf files.

    • The first one will be the department proto file, for that, we will copy the same deparment.proto we have used in Department (Java) service and copy that inside directory root-directory/proto.
    • The second one will be the profile proto file for that we will create a new file named profile.proto inside directory root-directory/proto and add the following code:
  2. Now run the command inside the “proto” (containing proto files) directory:

protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
*.proto

It will generate the Golang code based on the “profile.proto” file.

Implement Profile Logic:

  1. Create a new file in the directory root-directory/profile named profile.go and add the following code:
package profile

import (
 "context"
 "grpc-demo/proto"

 "google.golang.org/grpc"
 "google.golang.org/grpc/credentials/insecure"
)

type User struct {
 Age int64
}

var UserDetails = map[string]User{
   "John": {
    Age: 24,
   },
   "Mark": {
    Age: 25,
   },
}

type Server struct {
 proto.UnimplementedProfileServer
}

// Extending Grpc generated code to add custom implementation
func (s *Server) GetProfileByUserName(ctx context.Context, pr *proto.FetchProfileRequest) (*proto.FetchProfileResponse, error) {
   conn, err := grpc.Dial("localhost:9090", grpc.WithTransportCredentials(insecure.NewCredentials()))
   if err != nil {
    return nil, err
   }

  // Creating Department gRPC client to fetch department details from Department ser
   client := proto.NewDepartmentServiceClient(conn)
   dept, err := client.GetDepartmentByUserName(context.Background(), &proto.FetchDepartmentRequest{Name: pr.Name})
   if err != nil {
    return nil, err
   }

   return &proto.FetchProfileResponse{
    Name:       pr.Name,
    Age:        UserDetails[pr.Name].Age,
    Department: dept.Name,
   }, nil
}
  1. Now we need to start our gRPC server, for that we will create a file named main.go at root-directory and add the following code:
package main

import (
 "google.golang.org/grpc"
 "grpc-demo/profile"
 "grpc-demo/proto"
 "log"
 "net"
)

func main() {
 println("gRPC profile proto in Go")

 listener, err := net.Listen("tcp", ":9000")
 if err != nil {
  panic(err)
 }

 s := grpc.NewServer()

// Binding Our Implementations in this case profile service
 proto.RegisterProfileServer(s, &profile.Server{})
 if err := s.Serve(listener); err != nil {
  log.Fatalf("failed to serve: %v", err)
 }
}
  1. We are done now we can test out application, we will use Postman again to test our Profile service.
grpc-postman-golang

We can see that we are obtaining department details from department service and using them to generate our profile response.

Stay in sync with gRPC and cutting-edge tech updates for seamless, modern communication, subscribe to the free newsletter today for updates and insights delivered straight to your inbox.