Backend

99 Ngày Spring — Ngày 86: gRPC với Spring

SSite Admin
21 tháng 10, 2026 3 phút đọc 5 lượt xem
99 Ngày Spring — Ngày 86: gRPC với Spring

REST với JSON dễ đọc và dễ thử, nhưng hợp đồng giữa hai service thường chỉ nằm trong tài liệu. gRPC đặt hợp đồng lên trước: bạn viết file .proto, công cụ sinh code cho cả client lẫn server, và dữ liệu đi trên HTTP/2 ở dạng nhị phân Protocol Buffers. Nó hợp nhất cho giao tiếp nội bộ giữa các service.

gRPC với Spring

1. File proto là hợp đồng

File .proto khai báo message và service. Mỗi trường có một số thứ tự; chính con số đó, không phải tên trường, được ghi vào dữ liệu nhị phân. Vì vậy đổi tên trường thì an toàn, còn đổi hoặc dùng lại số của một trường đã xóa sẽ làm client cũ đọc sai. Thêm trường mới với số mới là thay đổi tương thích ngược. gRPC có bốn kiểu gọi: unary, server streaming, client streaming và streaming hai chiều.

syntax = "proto3";
package shop.orders.v1;
option java_multiple_files = true;
option java_package = "vn.motdev.orders.grpc";

service OrderService {
  rpc GetOrder (GetOrderRequest) returns (OrderReply);
  rpc WatchOrder (GetOrderRequest) returns (stream OrderReply);
}
message GetOrderRequest { string id = 1; }
message OrderReply {
  string id = 1;
  string status = 2;
  int64 total_cents = 3;
}

2. Server: kế thừa lớp được sinh

Plugin protobuf của Maven hoặc Gradle sinh lớp nền OrderServiceGrpc.OrderServiceImplBase lúc build. Bạn kế thừa và cài đặt từng phương thức. Với dự án Spring gRPC chính thức, mọi bean kiểu BindableService được tự đăng ký vào server gRPC; hãy chọn phiên bản starter khớp với Spring Boot đang dùng. Kết quả được trả qua StreamObserver: onNext cho từng phần tử, onCompleted để kết thúc, onError để báo lỗi.

@Service
class OrderGrpcService extends OrderServiceGrpc.OrderServiceImplBase {
    private final OrderRepository orders;
    OrderGrpcService(OrderRepository orders) { this.orders = orders; }

    @Override
    public void getOrder(GetOrderRequest request,
                         StreamObserver<OrderReply> observer) {
        orders.findById(request.getId()).ifPresentOrElse(order -> {
            observer.onNext(OrderReply.newBuilder()
                .setId(order.id()).setStatus(order.status())
                .setTotalCents(order.totalCents()).build());
            observer.onCompleted();
        }, () -> observer.onError(Status.NOT_FOUND
            .withDescription("Order not found").asRuntimeException()));
    }
}

3. Client, deadline và status code

Client gọi qua stub được sinh từ cùng file proto, nên sai kiểu bị bắt lúc biên dịch. gRPC không dùng mã HTTP mà có bộ status riêng: NOT_FOUND, INVALID_ARGUMENT, UNAVAILABLE, DEADLINE_EXCEEDED. Mỗi lời gọi nên có deadline; deadline được truyền tiếp qua các service phía sau, nên một request đã hết hạn sẽ không tiếp tục tiêu tài nguyên. Chỉ retry với status UNAVAILABLE và với thao tác idempotent.

OrderReply reply = stub
    .withDeadlineAfter(500, TimeUnit.MILLISECONDS)
    .getOrder(GetOrderRequest.newBuilder().setId("42").build());

// StatusRuntimeException carries the status:
// e.getStatus().getCode() == Status.Code.NOT_FOUND

4. gRPC hay REST

gRPC thắng ở hợp đồng chặt, payload nhỏ, streaming và sinh code đa ngôn ngữ. REST thắng ở chỗ trình duyệt gọi trực tiếp được, đọc được bằng mắt, cache HTTP và công cụ phổ biến. Nhiều hệ thống dùng cả hai: REST hoặc GraphQL ở biên cho client bên ngoài, gRPC giữa các service nội bộ.

  • Quản lý file proto như mã nguồn dùng chung: có review và kiểm tra tương thích ngược.

  • Dùng reserved cho số và tên trường đã xóa.

  • Load balancer phải hiểu HTTP/2; cân bằng theo kết nối sẽ dồn tải vào một instance.

  • Bật reflection hoặc dùng grpcurl để thử thủ công khi phát triển.

gRPC đáng dùng khi hai đầu đều do bạn kiểm soát và hợp đồng cần được máy kiểm tra. Ngày 87 quay lại mô hình luồng: virtual thread trong Spring Boot.

Tài liệu đối chiếu

  • https://grpc.io/docs/languages/java/basics/

  • https://docs.spring.io/spring-grpc/reference/

S

Site Admin

Engineer and writer. Building things with TypeScript and distributed systems.

Bình luận (0)

Bạn cần đăng nhập bằng Google để bình luận.

Hãy là người bình luận đầu tiên.

Bài viết liên quan

99 Ngày Spring — Ngày 87: Virtual Threads trong Spring Boot

Bật virtual thread bằng một thuộc tính, giới hạn chuyển sang đâu, hiện tượng pinning và so sánh với WebFlux.

22 thg 10, 20263 phút6
99 Ngày Java — Ngày 87: Gradle

build.gradle.kts, cấu hình phụ thuộc, task và build tăng dần, và so sánh thực tế với Maven.

22 thg 10, 20263 phút6
99 Ngày Java — Ngày 86: Maven

POM và tọa độ, dependency scope, phụ thuộc bắc cầu, vòng đời build và BOM để quản lý phiên bản.

21 thg 10, 20263 phút5