Implementing gRPC Services
1. Adding gRPC Dependencies (grpc-spring-boot-starter)
Example: gRPC server dependency
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>
2. Defining Protocol Buffers (.proto files)
Example: Proto definition for GreeterService
syntax = "proto3";
package com.example.grpc;
option java_multiple_files = true;
service GreeterService {
rpc SayHello (HelloRequest) returns (HelloReply);
rpc SayHellos (HelloRequest) returns (stream HelloReply);
}
message HelloRequest { string name = 1; }
message HelloReply { string message = 1; }
3. Generating Java Classes from Proto Files
Example: Protobuf Maven plugin
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.25.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.62.2:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
</plugin>
4. Creating gRPC Server Services (@GrpcService)
Example: Unary gRPC service implementation
@GrpcService
public class GreeterServiceImpl extends GreeterServiceGrpc.GreeterServiceImplBase {
@Override public void sayHello(HelloRequest req, StreamObserver<HelloReply> out) {
out.onNext(HelloReply.newBuilder().setMessage("Hello " + req.getName()).build());
out.onCompleted();
}
}
5. Implementing Unary RPC Methods
| Pattern | Client → Server |
|---|---|
| Unary | 1 req → 1 resp |
| Server streaming | 1 req → many resp |
| Client streaming | many req → 1 resp |
| Bidirectional | many req ↔ many resp |
6. Implementing Server Streaming RPC
Example: Server-streaming RPC
@Override public void sayHellos(HelloRequest req, StreamObserver<HelloReply> out) {
for (int i = 0; i < 5; i++)
out.onNext(HelloReply.newBuilder().setMessage("#" + i + " " + req.getName()).build());
out.onCompleted();
}
7. Implementing Client Streaming RPC
Example: Client-streaming RPC
@Override public StreamObserver<HelloRequest> collect(StreamObserver<HelloReply> out) {
return new StreamObserver<>() {
final List<String> names = new ArrayList<>();
public void onNext(HelloRequest r) { names.add(r.getName()); }
public void onError(Throwable t) {}
public void onCompleted() {
out.onNext(HelloReply.newBuilder().setMessage("Hello " + names).build());
out.onCompleted();
}
};
}
8. Implementing Bidirectional Streaming RPC
Example: Bidirectional streaming RPC
@Override public StreamObserver<HelloRequest> chat(StreamObserver<HelloReply> out) {
return new StreamObserver<>() {
public void onNext(HelloRequest r) {
out.onNext(HelloReply.newBuilder().setMessage("Echo " + r.getName()).build());
}
public void onError(Throwable t) { out.onError(t); }
public void onCompleted() { out.onCompleted(); }
};
}
9. Creating gRPC Clients (gRPC stubs)
Example: gRPC client with blocking stub
@Service
public class GreeterClient {
@GrpcClient("greeter") private GreeterServiceGrpc.GreeterServiceBlockingStub stub;
public String hello(String name) {
return stub.sayHello(HelloRequest.newBuilder().setName(name).build()).getMessage();
}
}
// application.yml: grpc.client.greeter.address=static://localhost:9090
10. Handling gRPC Exceptions and Status Codes
| Status | Meaning |
|---|---|
INVALID_ARGUMENT |
Bad input |
NOT_FOUND |
Resource missing |
UNAUTHENTICATED |
No/invalid creds |
PERMISSION_DENIED |
Forbidden |
DEADLINE_EXCEEDED |
Timeout |
UNAVAILABLE |
Server down/retryable |
INTERNAL |
Server error |
Example: Return gRPC NOT_FOUND status
out.onError(Status.NOT_FOUND.withDescription("user not found").asRuntimeException());
11. Implementing gRPC Interceptors
Example: Global gRPC server interceptor
@GrpcGlobalServerInterceptor
public class LoggingInterceptor implements ServerInterceptor {
@Override public <ReqT,RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT,RespT> call, Metadata md, ServerCallHandler<ReqT,RespT> next) {
log.info("gRPC {} from {}", call.getMethodDescriptor().getFullMethodName(),
call.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR));
return next.startCall(call, md);
}
}