Tích hợp GRPC

Mục tiêu
Chuyển từ:
order-service
gọiuser-service
qua Feign + HTTP REST
Sang:
order-service
gọiuser-service
qua gRPC để lấyUserDto
theouserId
.
Các bước thực hiện
1. Tạo file user.proto
Tạo tại user-service/src/main/proto/user.proto
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.example.userservice.grpc";
option java_outer_classname = "UserProto";
service UserGrpcService {
rpc GetUserById (GetUserRequest) returns (UserResponse);
}
message GetUserRequest {
int64 userId = 1;
}
message UserResponse {
int64 id = 1;
string name = 2;
string email = 3;
}
2. Cấu hình Gradle cho user-service
user-service/build.gradle
plugins {
id 'java'
id 'com.google.protobuf' version '0.9.4'
}
dependencies {
//grpc
implementation 'net.devh:grpc-server-spring-boot-starter:2.15.0.RELEASE'
implementation 'net.devh:grpc-client-spring-boot-starter:2.15.0.RELEASE'
// Ghim phiên bản GRPC core để tương thích
implementation 'io.grpc:grpc-core:1.54.0'
implementation 'io.grpc:grpc-netty-shaded:1.54.0'
implementation 'io.grpc:grpc-protobuf:1.54.0'
implementation 'io.grpc:grpc-stub:1.54.0'
// Cho phép dùng javax.annotation
implementation 'javax.annotation:javax.annotation-api:1.3.2'
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.25.3"
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:1.64.0"
}
}
generateProtoTasks {
all().each { task ->
task.plugins {
grpc {}
}
}
}
}
3. Cài đặt gRPC server ở user-service
Tạo class UserGrpcServiceImpl
import com.example.userservice.model.User;
import com.example.userservice.repository.UserRepository;
import io.grpc.stub.StreamObserver;
import lombok.RequiredArgsConstructor;
import net.devh.boot.grpc.server.service.GrpcService;
@GrpcService
@RequiredArgsConstructor
public class UserGrpcServiceImpl extends UserGrpcServiceGrpc.UserGrpcServiceImplBase {
private final UserRepository userRepository;
@Override
public void getUserById(GetUserRequest request, StreamObserver<UserResponse> responseObserver) {
User user = userRepository.findById(request.getUserId())
.orElseThrow(() -> new RuntimeException("User not found"));
UserResponse response = UserResponse.newBuilder()
.setId(user.getId())
.setName(user.getName())
.setEmail(user.getEmail())
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
4. Cấu hình gRPC client tại order-service
order-service/build.gradle
plugins {
id 'java'
id 'com.google.protobuf' version '0.9.4'
}
dependencies {
//grpc
implementation 'net.devh:grpc-client-spring-boot-starter:2.15.0.RELEASE'
// Ghim phiên bản GRPC core để tương thích
implementation 'io.grpc:grpc-core:1.54.0'
implementation 'io.grpc:grpc-netty-shaded:1.54.0'
implementation 'io.grpc:grpc-protobuf:1.54.0'
implementation 'io.grpc:grpc-stub:1.54.0'
// Cho phép dùng javax.annotation
implementation 'javax.annotation:javax.annotation-api:1.3.2'
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.25.3"
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:1.64.0"
}
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {}
}
task.plugins {
grpc {}
}
}
}
}
👉 Copy file
user.proto
giống như bên user-service vào:order-service/src/main/proto/user.proto
( nhớ thay đổi package name nha)
5. Cấu hình application.yml
user-service
grpc:
server:
port: 9090
order-service
grpc:
client:
user-service:
address: static://localhost:9090
negotiationType: plaintext
6. Tạo gRPC client trong order-service
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Service;
@Service
public class GrpcUserClient {
@GrpcClient("user-service")
private UserGrpcServiceGrpc.UserGrpcServiceBlockingStub userStub;
public UserResponse getUserById(Long id) {
GetUserRequest request = GetUserRequest.newBuilder().setUserId(id).build();
return userStub.getUserById(request);
}
}
7. Gọi gRPC thay cho Feign trong OrderController
//call gprc client
private final GrpcUserClient grpcUserClient;
@GetMapping("/grpc/{id}")
public OrderResponse getOrderGrpc(@PathVariable Long id) {
Order order = orderRepository.findById(id).orElseThrow();
// Gọi gRPC client để lấy thông tin user từ user-service
UserResponse user = grpcUserClient.getUserById(order.getUserId());
UserDto userDto = new UserDto(user.getId(), user.getName(), user.getEmail());
return new OrderResponse(order.getId(), order.getProduct(), order.getPrice(), userDto);
}
Test:
Gọi gRPC API để lấy order + user:
http://localhost:8082/api/orders/grpc/1
Kết quả:
{
"orderId": 1,
"product": "Macbook Pro",
"price": 2500.0,
"user": {
"id": 1,
"name": "user1",
"email": "holyne@gmail.com"
}
}
Tổng kết
Đây là luồng khi client gọi đến gRPC server để lấy thông tin user:
Client gọi getUserById(id)
↓
gRPC server nhận request (GetUserRequest)
↓
Tìm trong DB (userRepository.findById)
↓
Tạo response (UserResponse)
↓
Gửi lại cho client (onNext + onCompleted)
Tóm tắt flow gRPC giữa 2 services
order-service
gọiGrpcUserClient.getUserById(id)
Tạo
GetUserRequest
Gửi RPC request đến
user-service
user-service
xử lý trongUserGrpcServiceImpl
Trả về
UserResponse
order-service
nhận được kết quả
Ưu điểm của gRPC trong trường hợp này:
Gọi hàm từ xa như gọi local method (
userStub.getUserById
)Truyền data hiệu quả (binary, nhẹ, nhanh)
Có type-safe rõ ràng (
UserResponse
,GetUserRequest
...)
So sánh Feign vs gRPC trong hệ thống của bạn
Tiêu chí | Feign (REST) | gRPC |
Tốc độ | Trung bình (JSON, HTTP/1.1) | Nhanh hơn (Protocol Buffers, HTTP/2) |
Tự động sinh mã | Không (dùng interface thủ công) | Có (dùng .proto ) |
Kết nối services | Dễ dùng (vì là REST) | Phức tạp hơn nhưng tối ưu |
Gỡ lỗi | Dễ (xem được JSON rõ ràng) | Khó hơn (phải dùng tools riêng) |
Phù hợp | Giao tiếp với client/web | Giao tiếp service nội bộ tốc độ cao |
Gợi ý nâng cao
Tách
proto/
thành module dùng chung nhưcommon-proto
Dùng protobuf plugin caching để không build lại toàn bộ mỗi lần
Subscribe to my newsletter
Read articles from Holy_Dev directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Holy_Dev
Holy_Dev
strong desire for learning new things