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
- Generate Java classes from .proto file:
mvn compile
- Start the server:
mvn exec:java -Dexec.mainClass="com.example.grpc.server.GreetingServer"
- 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
- Service Definition: Protocol Buffers define the service contract
- Server Implementation: Extend the generated base class and implement service methods
- Client Stubs: Use generated client stubs to make RPC calls
- Communication: HTTP/2 transport with Protocol Buffers serialization
- 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.