Building a gRPC Server and Client in Java

gRPC is a modern, high-performance RPC (Remote Procedure Call) framework that can run in any environment. It uses HTTP/2 for transport, Protocol Buffers as the interface description language, and provides features such as authentication, bidirectional streaming and flow control.

Overview

In this article, we'll create a simple gRPC service in Java that demonstrates:

  • Defining a service using Protocol Buffers
  • Implementing a gRPC server
  • Creating a gRPC client to call the service
  • Basic unary RPC calls

Prerequisites

  • Java 8 or higher
  • Maven or Gradle build tool
  • Protocol Buffers compiler (protoc)

Step 1: Define the Service with Protocol Buffers

First, create a .proto file that defines your service and messages:

syntax = "proto3";
option java_package = "com.example.grpc";
option java_multiple_files = true;
service GreetingService {
rpc Greet (GreetingRequest) returns (GreetingResponse);
}
message GreetingRequest {
string name = 1;
}
message GreetingResponse {
string greeting = 1;
}

Step 2: Project Setup and Dependencies

Add the following dependencies to your pom.xml if using Maven:

<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.58.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.58.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.58.0</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.1</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.24.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.58.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Step 3: Implement the gRPC Server

Create the server implementation:

package com.example.grpc.server;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import com.example.grpc.GreetingRequest;
import com.example.grpc.GreetingResponse;
import com.example.grpc.GreetingServiceGrpc;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class GreetingServer {
private Server server;
private final int port;
public GreetingServer(int port) {
this.port = port;
}
public void start() throws IOException {
server = ServerBuilder.forPort(port)
.addService(new GreetingServiceImpl())
.build()
.start();
System.out.println("Server started, listening on " + port);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.err.println("*** shutting down gRPC server since JVM is shutting down");
try {
GreetingServer.this.stop();
} catch (InterruptedException e) {
e.printStackTrace(System.err);
}
System.err.println("*** server shut down");
}));
}
public void stop() throws InterruptedException {
if (server != null) {
server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
}
}
public void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
static class GreetingServiceImpl extends GreetingServiceGrpc.GreetingServiceImplBase {
@Override
public void greet(GreetingRequest request, StreamObserver<GreetingResponse> responseObserver) {
String greeting = "Hello, " + request.getName() + "! Welcome to gRPC.";
GreetingResponse response = GreetingResponse.newBuilder()
.setGreeting(greeting)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
public static void main(String[] args) throws IOException, InterruptedException {
GreetingServer server = new GreetingServer(50051);
server.start();
server.blockUntilShutdown();
}
}

Step 4: Implement the gRPC Client

Create the client implementation:

package com.example.grpc.client;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import com.example.grpc.GreetingRequest;
import com.example.grpc.GreetingResponse;
import com.example.grpc.GreetingServiceGrpc;
import java.util.concurrent.TimeUnit;
public class GreetingClient {
private final ManagedChannel channel;
private final GreetingServiceGrpc.GreetingServiceBlockingStub blockingStub;
public GreetingClient(String host, int port) {
this.channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext()
.build();
this.blockingStub = GreetingServiceGrpc.newBlockingStub(channel);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
public void greet(String name) {
System.out.println("Sending greeting request for: " + name);
GreetingRequest request = GreetingRequest.newBuilder()
.setName(name)
.build();
GreetingResponse response = blockingStub.greet(request);
System.out.println("Received: " + response.getGreeting());
}
public static void main(String[] args) throws InterruptedException {
GreetingClient client = new GreetingClient("localhost", 50051);
try {
// Test with different names
client.greet("Alice");
client.greet("Bob");
client.greet("Charlie");
} finally {
client.shutdown();
}
}
}

Step 5: Build and Run

  1. Generate Java classes from .proto file:
   mvn compile
  1. Start the server:
   mvn exec:java -Dexec.mainClass="com.example.grpc.server.GreetingServer"
  1. Run the client (in a separate terminal):
   mvn exec:java -Dexec.mainClass="com.example.grpc.client.GreetingClient"

Expected Output

Server output:

Server started, listening on 50051

Client output:

Sending greeting request for: Alice
Received: Hello, Alice! Welcome to gRPC.
Sending greeting request for: Bob
Received: Hello, Bob! Welcome to gRPC.
Sending greeting request for: Charlie
Received: Hello, Charlie! Welcome to gRPC.

Key gRPC Concepts Demonstrated

  1. Service Definition: Protocol Buffers define the service contract
  2. Server Implementation: Extend the generated base class and implement service methods
  3. Client Stubs: Use generated client stubs to make RPC calls
  4. Communication: HTTP/2 transport with Protocol Buffers serialization
  5. Lifecycle Management: Proper startup and shutdown of gRPC components

Advanced Features

gRPC supports several advanced features you can explore:

  • Streaming RPCs: Client streaming, server streaming, and bidirectional streaming
  • Error Handling: Status codes and error details
  • Deadlines/Timeouts: Setting timeouts for RPC calls
  • Interceptors: For cross-cutting concerns like logging, authentication
  • Load Balancing: Client-side load balancing

This example provides a solid foundation for building more complex gRPC services in Java. The type-safe API, performance benefits, and cross-language support make gRPC an excellent choice for microservices and distributed systems.

Leave a Reply

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


Macro Nepal Helper