gRPC và Protobuf thực chiến cho backend
Sê-ri 12 phần về gRPC và Protocol Buffers dưới góc nhìn lập trình viên backend: không chỉ cú pháp mà DỰNG SERVER + CLIENT THẬT bằng Go trong Docker và ĐO THẬT — kích thước wire Protobuf vs JSON, bốn kiểu RPC (unary/streaming), tiến hoá schema bằng field number, deadline/context, interceptor, status code lỗi, metadata/auth, streaming flow control, load balancing client-side, health check/reflection, graceful shutdown. Mỗi bài giải thích cơ chế rồi chứng minh bằng số liệu đo được, nêu rõ đánh đổi so với REST.
12/12 phần đã đăng
Lập trình
1
gRPC cho backend từ con số 0: gọi hàm từ xa như hàm nội bộ, và Protobuf nhỏ hơn JSON 2 lần
gRPC không phải REST nhanh hơn — nó là một mô hình khác: định nghĩa dịch vụ trong .proto, protoc sinh code type-safe cho cả server lẫn client, chạy trên HTTP/2 với payload nhị phân Protobuf. Bài mở màn sê-ri dựng server+client gRPC thật bằng Go trong Docker, gọi một unary RPC, và đo thật: cùng message Order, Protobuf 41 byte so với JSON 88 byte, 1000 call hết 42ms (~23.800 call/giây localhost).
22/09/2026
· 6 phút đọc
2
Protobuf wire format mổ đến từng byte: vì sao nó nhỏ hơn JSON 3,5 lần và message rỗng tốn 0 byte
Protobuf nhỏ hơn JSON không phải nhờ phép màu — mà nhờ ba quyết định thiết kế gọn gàng. Bài này dùng proto.Marshal rồi hexdump từng byte trong go-lab để giải mã: mỗi field là một tag (field_number dịch bit cộng wire_type) rồi tới value, số nguyên mã hoá bằng varint (số nhỏ chỉ 1 byte), và field mang giá trị mặc định không được ghi lên dây chút nào. Kết quả thật: cùng dữ liệu, JSON 46 byte còn Protobuf 13 byte, message rỗng tốn 0 byte.
22/09/2026
· 6 phút đọc
3
Bốn kiểu RPC của gRPC: unary, server/client/bidirectional streaming — thứ REST không có sẵn
REST chỉ có một kiểu: một request, một response. gRPC có bốn, nhờ HTTP/2 cho phép streaming hai chiều ngay trong contract. Bài này chạy thật cả bốn trong go-lab: unary Double(21) trả 42; server streaming CountUp(5) trả về 5 message; client streaming SumAll gửi 4 nhận 1 (total=100); bidirectional RunningSum gửi/nhận xen kẽ 3 lần. Chỉ cần thêm từ khoá stream vào .proto, và biết khi nào dùng kiểu nào.
22/09/2026
· 5 phút đọc
4
Tiến hoá schema Protobuf: vì sao field number là bất biến, và unknown field cứu dữ liệu qua tay service cũ
Protobuf nhận diện field bằng SỐ chứ không bằng tên — hiểu điều này là hiểu toàn bộ quy tắc tiến hoá schema. Bài này đo thật trong go-lab ba kịch bản: V1 ghi, V2 (thêm field) đọc được, field mới lấy default; V2 ghi có email, service V1 cũ đọc không thấy nhưng GIỮ LẠI email qua round-trip nhờ unknown field; và đổi field number khiến amount biến mất hoàn toàn. Rút ra luật quan trọng nhất của Protobuf.
22/09/2026
· 6 phút đọc
5
Deadline và context trong gRPC: hạn chót lan truyền cả chuỗi gọi, và server dừng sớm để không phí tài nguyên
Một request không bao giờ nên treo vô hạn. gRPC gắn deadline vào context, gửi kèm request, để mọi tầng biết còn bao lâu và dừng đúng lúc. Bài này đo thật trong go-lab: call deadline 100ms vào handler làm 500ms trả về sau đúng 100ms với code DeadlineExceeded (không chờ hết việc); và server đọc ctx.Done() để dừng sớm thay vì chạy nốt 500ms vô ích. Hiểu cơ chế này để chống quá tải lan rộng.
22/09/2026
· 5 phút đọc
6
Interceptor trong gRPC: middleware gắn một lần cho mọi RPC, và recover panic giữ server sống sót
Logging, auth, metrics, bắt panic — bạn không nhét chúng vào từng handler mà bọc quanh tất cả bằng interceptor, middleware của gRPC. Bài này đo thật trong go-lab: chain hai interceptor (log ngoài, recover trong) chạy lồng nhau như vỏ hành; một handler panic được interceptor recover bắt lại, client nhận lỗi Internal gọn gàng thay vì connection chết, và server vẫn phục vụ request tiếp theo bình thường.
22/09/2026
· 5 phút đọc
7
Xử lý lỗi trong gRPC: status code chuẩn và rich error details — để client xử lý đúng thay vì đoán chuỗi
Trả lỗi bằng một chuỗi text buộc client phải đoán và regex — mong manh và dễ vỡ. gRPC có bộ status code chuẩn hoá (NotFound, InvalidArgument, Unavailable...) để client rẽ nhánh đúng, và rich error details để gửi lỗi CÓ CẤU TRÚC. Bài này đo thật trong go-lab: Get id không tồn tại trả NotFound, id âm trả InvalidArgument; và Create với input xấu trả về danh sách field vi phạm (email, age) để client đọc type-safe thay vì parse câu lỗi.
22/09/2026
· 5 phút đọc
8
Metadata và xác thực trong gRPC: truyền token như header HTTP, kiểm auth một chỗ cho mọi RPC
gRPC không có khái niệm header riêng — nó dùng metadata, cặp key-value gửi kèm mỗi request giống HTTP header. Bài này đo thật trong go-lab: client gắn token qua metadata.AppendToOutgoingContext, server đọc trong một interceptor auth tập trung. Gọi không token trả Unauthenticated, token sai trả PermissionDenied, token đúng mới cho handler chạy và server lấy được user=alice từ token. Và vì sao metadata bắt buộc chạy trên TLS.
22/09/2026
· 5 phút đọc
9
Hiệu năng streaming gRPC: vì sao một stream nhanh gấp 61 lần 50.000 unary call, và flow control giữ cho khỏi tràn
Khi cần gửi nhiều message, chọn một stream hay nhiều unary call tạo ra khác biệt khổng lồ. Bài này đo thật trong go-lab: gửi 50.000 message qua 50.000 unary call riêng lẻ mất 2,06 giây (24k msg/s), còn qua một server-stream chỉ 33,6 mili-giây (1,49 triệu msg/s) — nhanh gấp 61 lần. Giải thích vì sao per-call overhead giết hiệu năng, và HTTP/2 flow control điều tiết backpressure thế nào để stream không làm tràn bộ nhớ.
22/09/2026
· 6 phút đọc
10
Load balancing phía client trong gRPC: vì sao LB kiểu HTTP/1 dồn hết vào một server, và round-robin chia đều thế nào
Đặt một load balancer thường trước gRPC là một bẫy: vì gRPC giữ một kết nối HTTP/2 lâu dài, LB mức kết nối (L4) ghim mọi request vào đúng một backend. Bài này đo thật trong go-lab: chạy 3 server, dùng client-side round-robin, gọi 300 RPC — chia gần như hoàn hảo 100/99/101 giữa ba server. Hiểu vì sao gRPC cần cân tải ở mức RPC (client-side hoặc proxy L7), không phải mức kết nối.
22/09/2026
· 5 phút đọc
11
gRPC cho production: health check, reflection và graceful shutdown — ba thứ không có khi học nhưng bắt buộc khi chạy thật
Một service gRPC chạy được chưa phải chạy được trong production. Bài này đo thật trong go-lab ba tính năng vận hành: health check để orchestrator biết pod sống hay chết (SERVING và NOT_SERVING đổi được), server reflection để grpcurl liệt kê service mà không cần file .proto, và graceful shutdown để khi deploy không rớt request đang chạy — GracefulStop chờ request 800ms hoàn thành nhưng từ chối request mới ngay.
22/09/2026
· 5 phút đọc
12
Tổng kết sê-ri gRPC: khi nào chọn gRPC thay REST, và checklist đưa service lên production đúc kết từ 11 bài
Mười một bài, hàng chục phép đo thật — giờ gộp lại thành quyết định dùng được. Bài tổng kết này so gRPC với REST và GraphQL để biết khi nào chọn cái nào, một cây quyết định có nên dùng gRPC không, và một checklist đưa service gRPC lên production ánh xạ về từng bài: contract-first, field number bất biến, deadline, interceptor, status code, TLS, streaming đúng chỗ, load balancing, health check, graceful shutdown.
22/09/2026
· 6 phút đọc