
gRPC is an RPC (Remote Procedure Call) framework originally developed by Google in 2015 and is now a part of the Cloud Native Computing Foundation (CNCF). It utilizes the HTTP/2 protocol, which is significantly faster than HTTP/1.1, and uses Protocol Buffers (Protobuf) as its Interface Definition Language (IDL) to compile the code needed for development.
A major benefit of Protocol Buffers is that you only need to write the Protobuf definition once, and it can be compiled to connect with multiple programming languages. Currently, Protobuf supports C++, Java, Python, Objective-C, C#, JavaScript, Ruby, Go, PHP, and Dart. As you can see, it covers almost all popular modern languages.
In addition to being faster than RESTful APIs, gRPC exchanges data in a binary format, unlike RESTful APIs which use text-based JSON. This binary serialization makes gRPC exceptionally fast. However, it does trade off some of the "loose coupling" benefits found in REST, as both the client and server must implement Protobuf and gRPC to communicate.
To get started with gRPC, you first write a Protobuf file to define the data exchange format. Then, you compile it into your target programming language for both the Server and Client sides.
This article will demonstrate how to experiment with gRPC using Spring Boot and Gradle for service-to-service communication.
Server Dependencies [build.gradle]
plugins {
id 'org.springframework.boot' version '2.2.2.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
// grpc step 1
id 'com.google.protobuf' version '0.8.8'
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
// grpc step 2
def grpcVersion = '1.27.1'
def protobufVersion = '3.11.0'
def protocVersion = protobufVersion
def lombokVersion = '1.18.12'
dependencies {
// grpc step 3
compile "io.github.lognet:grpc-spring-boot-starter:3.5.1"
implementation "org.springframework.boot:spring-boot-starter-web"
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude group: "org.junit.vintage", module: "junit-vintage-engine"
}
implementation "io.grpc:grpc-netty-shaded:${grpcVersion}"
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
implementation "io.grpc:grpc-stub:${grpcVersion}"
compileOnly "org.projectlombok:lombok:${lombokVersion}"
annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
testCompileOnly "org.projectlombok:lombok:${lombokVersion}"
testAnnotationProcessor "org.projectlombok:lombok:${lombokVersion}"
}
test {
useJUnitPlatform()
}
// grpc step 4
protobuf {
protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
plugins {
grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
}
generateProtoTasks {
all()*.plugins { grpc {} }
}
}
// grpc step 5: Inform IDE about the generated code
sourceSets {
main {
java {
srcDirs 'build/generated/source/proto/main/grpc'
srcDirs 'build/generated/source/proto/main/java'
}
}
}
Explanation of the steps:
-
Step 1: Define the plugins.
org.springframework.bootandio.spring.dependency-managementare for Spring Boot.com.google.protobufis used to utilize the protobuf compiler. -
Step 2: Define the version numbers for gRPC and Protobuf.
-
Step 3: Define dependencies.
-
grpc-spring-boot-starterallows using gRPC with Spring Boot to run it as a service. -
spring-boot-starter-webis the standard Spring Boot web starter. -
We exclude
junit-vintage-enginefrom testing as it is not needed. -
The
io.grpcdependencies are the core gRPC libraries.
-
-
Step 4: Create the protobuf compilation tasks.
-
Step 5: Define the generated file paths so the IDE can locate them. The default compilation output paths are
build/generated/source/proto/main/grpcandbuild/generated/source/proto/main/java.
Protocol Buffer [HelloService.proto]
syntax = "proto3";
option java_multiple_files = true;
package org.example.grpc.server;
message HelloRequest {
string firstName = 1;
string lastName = 2;
}
message HelloResponse {
string greeting = 1;
}
service HelloService {
rpc hello(HelloRequest) returns (HelloResponse);
}
-
syntax = "proto3";: Specifies the Protobuf version. -
option java_multiple_files = true;: Ensures that multiple Java files are generated. Without this, everything compiles into a single massive Java file. -
package: Sets the package name for the generated code. -
message HelloRequest/message HelloResponse: Defines the message structures, field names, and data types (similar to defining Model classes). -
service HelloService: Defines the service and its RPC methods, including request and response types.
After setting up the .proto file, run the Gradle build or the generateProto task. The generated files will be placed in build/generated/source/proto/main/.

Spring Boot gRPC Server Implement
Create the Spring Boot Application class:
package org.example.grpc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Create the gRPC Service implementation:
@GRpcService
public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {
@Override
public void hello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
System.out.println(request);
HelloResponse response = HelloResponse.newBuilder()
.setGreeting("GRPC Hello: " + request.getFirstName() + " " + request.getLastName())
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
-
@GRpcService: Comes from thegrpc-spring-boot-starterdependency, allowing Spring Boot to run gRPC as a service. -
extends HelloServiceGrpc.HelloServiceImplBase: Allows us to override the service methods defined in the Protobuf file. -
hello(...): This is the method signature generated by compiling the Protobuf file. -
HelloResponse.newBuilder()...build(): Objects are created using the builder pattern generated by the Protobuf compiler. -
StreamObserver<HelloResponse> responseObserver: Manages the state of the gRPC call (next, complete, error).-
responseObserver.onNext(response);: Sends the response back to the client. -
responseObserver.onCompleted();: Signals the client that the transaction is finished.
-
Server Configuration [application.properties]
server.port=8081
grpc.port=6081
-
server.port: The standard Spring Boot web port. -
grpc.port: The dedicated port for the gRPC service.
Setting up the Client
Client Dependencies & Protocol Buffer: You can use the exact same build.gradle dependencies and the same HelloService.proto file for the client side. This is a core advantage of gRPC—you share the exact same contract.
Spring Boot Client Application:
package grpc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Create the gRPC Client Component:
package grpc.app;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import org.example.grpc.server.HelloRequest;
import org.example.grpc.server.HelloResponse;
import org.example.grpc.server.HelloServiceGrpc;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class HelloServiceClient {
private final ManagedChannel channel;
private final HelloServiceGrpc.HelloServiceBlockingStub blockingStub;
public HelloServiceClient(@Value("${hello.service.endpoint}") String endpoint) {
// Create a communication channel to the server
this.channel = ManagedChannelBuilder.forTarget(endpoint)
.usePlaintext()
.build();
// Passing channels to code makes it easier to test and reuse
this.blockingStub = HelloServiceGrpc.newBlockingStub(this.channel);
}
public HelloResponse hello(String firstName, String lastName) {
HelloRequest request = HelloRequest.newBuilder()
.setFirstName(firstName)
.setLastName(lastName)
.build();
System.out.println(request);
HelloResponse response;
try {
response = blockingStub.hello(request);
} catch (StatusRuntimeException e) {
e.printStackTrace();
return null;
}
System.out.println(response);
return response;
}
}
-
ManagedChannel channel: Establishes the communication link to the gRPC server. -
HelloServiceBlockingStub blockingStub: Used to invoke the methods defined in the Protobuf file synchronously. -
ManagedChannelBuilder.forTarget(endpoint).usePlaintext().build(): Initializes the channel targeting the server endpoint (using plaintext for testing, without TLS). -
blockingStub.hello(request): Calls the remote gRPC service method and awaits the response.
Example REST Controller to Trigger the gRPC Client:
package grpc;
import grpc.app.HelloServiceClient;
import grpc.app.HelloServiceFeignClient;
import org.example.grpc.server.HelloResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
private HelloServiceClient helloServiceClient;
@GetMapping("/hello")
public String hello(@RequestParam("f") String firstName, @RequestParam("l") String lastName) {
HelloResponse response = helloServiceClient.hello(firstName, lastName);
if (response == null) {
return "NullPointerException";
} else {
return response.toString();
}
}
}
จากทั้งหมดที่ได้ลองพัฒนามา จะเห็นว่ามีทั้งความง่าย และความซับซ้อนที่ต้องทำความเข้าใจเพิ่มขึ้นอยู่ ก็ถือว่าเป็นทางเลือกใหม่ที่จะช่วยให้การทำงานร่วมกันระว่าง service ทำงานในไวขึ้น ซึ่งจากเทรนในช่วง 2 - 3 ปี ที่ผ่านมา ก็จะเห็นว่าเริ่มมีหลายๆ ประเทศใช้ gRPC กันมากขึ้น หรือแม้แต่ประเทศจีนที่มีการใช้งาน gRPC กันแทน restful เป็นส่วนใหญ่แล้ว