Suốt mười bài trước, ta xây và tối ưu service gRPC. Nhưng chạy được trong lab khác xa vận hành được trong production. Ba câu hỏi mà mọi service thật phải trả lời: Làm sao orchestrator (Kubernetes, load balancer) biết service còn sống để gửi traffic? Làm sao gọi thử/debug service khi không cầm sẵn file .proto? Và làm sao deploy (giết pod cũ, dựng pod mới) mà không rớt những request đang chạy dở? gRPC có đáp án chuẩn cho cả ba: health check, reflection, và graceful shutdown. Bài này (phần 11/12) đo thật cả ba trong go-lab.
Ba tính năng vận hành

Hình 1: Health check (service chuẩn cho orchestrator thăm dò, đổi SERVING/NOT_SERVING); reflection (reflection.Register cho grpcurl liệt kê service không cần .proto); graceful shutdown (GracefulStop chờ request đang chạy xong và từ chối request mới, khác Stop cắt ngay).
- Health check: đăng ký một service chuẩn
grpc.health.v1.Health. Orchestrator gọiCheck()định kỳ để biết pod sống/chết; bạn đặtSetServingStatus(NOT_SERVING)khi bảo trì để LB ngừng gửi traffic trước khi tắt. - Reflection:
reflection.Register(g)cho phép bất kỳ công cụ nào (grpcurl) hỏi server "có service/method gì" — gọi thử và debug không cần cầm file .proto. - Graceful shutdown:
GracefulStop()chờ các request đang chạy hoàn thành và từ chối request mới, rồi mới tắt — khácStop()cắt ngay (làm rớt request đang chạy).
Đo thật: cả ba trong go-lab

Hình 2: Đo thật. (1) Health: Check trả SERVING, sau SetServingStatus(NOT_SERVING) trả NOT_SERVING. (2) Reflection: grpcurl list liệt kê các service (gồm svc.Worker) và method svc.Worker.Do mà không cần .proto; health Check qua grpcurl trả SERVING. (3) Graceful shutdown: gọi lúc request 800ms đang chạy — request mới bị từ chối, request đang chạy vẫn hoàn thành.
Kết quả thật:
- ① Health check đổi trạng thái được:
Check(svc.Worker)trảSERVING; sau khi gọiSetServingStatus(NOT_SERVING),ChecktrảNOT_SERVING. Trong Kubernetes, bạn cấu hình gRPC health probe trỏ vào đây — pod báoNOT_SERVINGsẽ bị gỡ khỏi danh sách nhận traffic mà không cần giết. - ② Reflection hoạt động với grpcurl:
grpcurl -plaintext 127.0.0.1:50122 listtrả vềgrpc.health.v1.Health,grpc.reflection.v1.ServerReflection, vàsvc.Worker.list svc.Workertrảsvc.Worker.Do. Vàgrpcurl ... Health/Checktrả{"status":"SERVING"}. Toàn bộ không cần file .proto — server tự mô tả chính nó. Đây là thứ khiến debug gRPC (vốn khó vì payload nhị phân, bài grpc-02) trở nên dễ thở. - ③ Graceful shutdown chờ đúng chỗ: tại t+150ms, một request 800ms đang chạy, mình gọi
GracefulStop(). Tại t+250ms, một request mới bị từ chối (error). Nhưng request 800ms đang chạy vẫn hoàn thành bình thường ("xong sau 800ms") rồi server mới tắt. So vớiStop()cắt ngay (request 800ms sẽ bị rớt giữa chừng),GracefulStoplà thứ cho phép deploy/scale-down mà không một client nào nhận lỗi oan.
Đánh đổi cần cân nhắc
Health check nên phản ánh khả năng phục vụ thật, không chỉ "process còn sống". Trả SERVING chỉ vì tiến trình chưa chết là bẫy: nếu service mất kết nối database mà vẫn báo SERVING, orchestrator gửi traffic tới một pod không làm được việc. Health check tốt nên kiểm cả các phụ thuộc thiết yếu (DB, cache) — nhưng cẩn thận đừng để nó quá nặng (gọi liên tục) hoặc quá nhạy (một blip mạng làm cả pod bị gỡ). Cân bằng giữa "trung thực" và "ổn định".
Reflection tiện cho dev nhưng cân nhắc tắt ở production công khai. Reflection phơi bày toàn bộ API surface của server cho bất kỳ ai gọi được — tiện khi debug nội bộ, nhưng với một endpoint công khai, nó cho kẻ tấn công bản đồ đầy đủ các method. Nhiều nơi bật reflection ở môi trường dev/staging và tắt (hoặc chặn sau auth) ở production internet-facing. Đây là đánh đổi tiện-lợi vs lộ-thông-tin.
Graceful shutdown cần phối hợp với deadline và orchestrator. GracefulStop chờ request đang chạy vô thời hạn — nếu có một request treo (stream dài, bài grpc-03), nó chờ mãi. Thực tế cần: (1) đặt deadline cho mọi RPC (bài grpc-05) để không request nào treo vĩnh viễn, và (2) orchestrator cho một grace period (ví dụ terminationGracePeriodSeconds của K8s) rồi mới SIGKILL. Quy trình deploy đúng: báo NOT_SERVING → chờ LB ngừng gửi → GracefulStop → nếu quá grace period thì buộc tắt.
Ba ý mang về
- Health check cho orchestrator biết service sống và sẵn sàng. Đo thật:
ChecktrảSERVING, đổi được sangNOT_SERVINGbằngSetServingStatus. K8s/LB dùng nó để gửi hay ngừng traffic — và nên phản ánh khả năng phục vụ thật (gồm phụ thuộc thiết yếu), không chỉ "process còn sống". - Reflection cho gọi thử/debug không cần .proto. Đo thật:
grpcurl listliệt kê service và method, health Check trả SERVING — tất cả nhờreflection.Register. Rất tiện cho dev; cân nhắc tắt ở endpoint công khai vì nó lộ toàn bộ API. - Graceful shutdown giữ cho deploy không rớt request. Đo thật:
GracefulStopđể request 800ms đang chạy hoàn thành nhưng từ chối request mới ngay. Phối hợp với deadline (để không request nào treo) và grace period của orchestrator cho một quy trình deploy sạch.
Nguồn
- gRPC — Health Checking Protocol: https://github.com/grpc/grpc/blob/master/doc/health-checking.md
- gRPC Go — Server Reflection Tutorial: https://github.com/grpc/grpc-go/blob/master/Documentation/server-reflection-tutorial.md
- gRPC Go — GracefulStop (godoc): https://pkg.go.dev/google.golang.org/grpc#Server.GracefulStop
Phần sau là bài tổng kết sê-ri: khi nào chọn gRPC thay REST, bảng so sánh gRPC vs REST vs GraphQL, và một checklist những gì cần có trước khi đưa service gRPC lên production.