A high performance, open source universal RPC framework
Introduction
gRPC is a modern open source high performance Remote Procedure Call (RPC) framework, was initially created by Google, first public release at August 2016.
It helps to eliminate boilerplate code and connect polyglot services in and across data centers.
Overview
- Define .proto file.
- Generate server and client code.
- Create server application, implement the generated service and spawn the gRPC server, make request with BloomRPC.
- Create client application, make RPC call using generated stubs.
Define .proto file
gRPC use Protocol Buffer (a.k.a. Protobuf) for strict schema definition. It's where store your data and function contracts in the form of a proto file.
Let's create a HelloService.proto file for our sample HelloService.
// Which syntax this file uses.
syntax = "proto3";
// Specify the package we want to use for our generated Java classes.
package com.example.grpc;
// Everything will be generated in individual files.
// By default, the compiler generates all the Java code in a single Java file.
option java_multiple_files = true;
// Request payload
message HelloRequest {
string name = 1;
}
// Response payload
message HelloResponse {
string greeting = 1;
}
// Service contract
service HelloService {
rpc hello(HelloRequest) returns (HelloResponse);
}
In request/response payload, each attribute that goes into the message is defined, along with its type.
A unique number needs to be assigned to each attribute, called the tag.
Finally, the hello() operation accepts a unary request, and returns a unary response.
Dependencies (Gradle)
buildscript {
ext {
protobufVersion = '3.19.1'
protobufPluginVersion = '0.8.18'
grpcVersion = '1.42.1'
}
}
plugins {
id 'java'
id 'com.google.protobuf' version "${protobufPluginVersion}"
}
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
implementation 'net.devh:grpc-spring-boot-starter:2.14.0.RELEASE'
compileOnly 'jakarta.annotation:jakarta.annotation-api:1.3.5'
}
test {
useJUnitPlatform()
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:${protobufVersion}"
}
generatedFilesBaseDir = "$projectDir/src/generated"
clean {
delete generatedFilesBaseDir
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}
Generate server and client code
Let's build introduction-grpc project, the protoc-gen-grpc-java lib will generate the code into /$projectDir/src/generated (configurated in the gradle file)
Create server application
Create new module named server-side inside the introduction-grpc project.
Update the server-side gradle file
plugins {
id 'java'
}
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation project(path: ':') // Reference to parent project
implementation 'net.devh:grpc-server-spring-boot-starter:2.14.0.RELEASE' // gRPC server dependence
}
test {
useJUnitPlatform()
}
Implement the generated service
Create the HelloService.java implement HelloServiceGrpc.HelloServiceImplBase
package com.example.server.services;
import com.example.grpc.HelloRequest;
import com.example.grpc.HelloResponse;
import com.example.grpc.HelloServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
@GrpcService
public class HelloService extends HelloServiceGrpc.HelloServiceImplBase{
@Override
public void hello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
HelloResponse reply = HelloResponse.newBuilder()
.setGreeting("Hello ==> " + request.getName())
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
In the generate server code, we have generated the HelloServiceGrpc contain code base for server implement.
The hello function doesn't return HelloResponse. Instead, it takes the second argument as StreamObserver<HelloResponse>, which is a response observer, a callback for the server to call with its response.
gRPC uses builders for creating objects. It use HelloResponse.newBuilder() and set the greeting text to build a HelloResponse object. Then set this object to the responseObserver's onNext() method to send it to the client.
Finally, we'll need to call onCompleted() to specify that we’ve finished dealing with the RPC. Otherwise, the connection will be hung, and the client will just wait for more information to come in.
Spawn the gRPC server
We'll use spring boot for spawn the gRPC server.
package com.example.server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
Default, server will start at port 9090
Make request with BloomRPC
Install BloomRPC from this
Import the HelloService.proto file
Make the request
We can see the response in the right side.
Create client application
Create new module named client-side inside the introduction-grpc project.
Update the client-side gradle file
plugins {
id 'java'
id 'org.springframework.boot' version '3.1.0'
id 'io.spring.dependency-management' version '1.1.0'
}
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation project(path: ':') // Reference to parent project
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'net.devh:grpc-client-spring-boot-starter:2.14.0.RELEASE'
}
test {
useJUnitPlatform()
}
application.yml
server:
port: 8080
spring:
application:
name: grpc-client
grpc:
client:
grpc-client:
address: 'static://127.0.0.1:9090'
enableKeepAlive: true
keepAliveWithoutCalls: true
negotiationType: plaintext
We need a GrpcConfig configuration to prevent NullPointerException when using @GrpcClient
package com.example.client.configs;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.context.annotation.Configuration;
@Configuration
@ImportAutoConfiguration({
net.devh.boot.grpc.client.autoconfigure.GrpcClientAutoConfiguration.class,
net.devh.boot.grpc.client.autoconfigure.GrpcClientMetricAutoConfiguration.class,
net.devh.boot.grpc.client.autoconfigure.GrpcClientHealthAutoConfiguration.class,
net.devh.boot.grpc.client.autoconfigure.GrpcClientSecurityAutoConfiguration.class,
net.devh.boot.grpc.client.autoconfigure.GrpcClientTraceAutoConfiguration.class,
net.devh.boot.grpc.client.autoconfigure.GrpcDiscoveryClientAutoConfiguration.class,
net.devh.boot.grpc.common.autoconfigure.GrpcCommonCodecAutoConfiguration.class,
net.devh.boot.grpc.common.autoconfigure.GrpcCommonTraceAutoConfiguration.class,
})
public class GrpcConfig {
}
Implement HelloService.java
package com.example.client.services;
import com.example.grpc.HelloRequest;
import com.example.grpc.HelloServiceGrpc.HelloServiceBlockingStub;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Service;
@Service
public class HelloService {
@GrpcClient("grpc-client")
private HelloServiceBlockingStub helloServiceStub;
public String receiveGreeting(String name) {
HelloRequest request = HelloRequest.newBuilder()
.setName(name)
.build();
return helloServiceStub.hello(request).getGreeting();
}
}
You can see we specific the grpc-client value in annotation @GrpcClient. That mean we will use grpc-client connection in the application.yml, which point to server address 127.0.0.1:9090
We'll need to inject HelloServiceBlockingStub, which we'll use to make the actual remote call to hello()
The stub is the primary way for clients to interact with the server.
We'll pass the HelloRequest. We can use the auto-generated setters to set the name attributes of the HelloRequest object.
The server returns the HelloResponse object.
Alos we need to create a resource (api) to trigger gRPC request.
Spawn client.
Make a request to resource (api)
Conclusion
This post is quickly introduce the gRPC and how to implement with Spring boot.
There are a lot of things interest about gRPC.
The code is available in this.
References:
- https://www.baeldung.com/grpc-introduction
- https://yidongnan.github.io/grpc-spring-boot-starter/
- https://github.com/yidongnan/grpc-spring-boot-starter/tree/master/examples
Happy coding.