Implementing gRPC Communication
1. Defining Protocol Buffers
| Element | Syntax |
|---|---|
| Syntax version | syntax = "proto3"; |
| Package | package shop.v1; |
| Message | message Order { string id = 1; } |
| Field types | scalar (int32, string, bool), message, enum, map, repeated |
| Service | service OrderService { rpc Get(GetReq) returns (Order); } |
| Field numbers | 1–15: 1-byte tag (use for hot fields); never reuse |
| Reserved | reserved 4, 5; reserved "old_name"; |
Example: order.proto
syntax = "proto3";
package shop.v1;
service OrderService {
rpc Get(GetOrderRequest) returns (Order);
rpc List(ListOrdersRequest) returns (stream Order);
rpc Bulk(stream CreateOrder) returns (BulkResult);
rpc Chat(stream Event) returns (stream Event);
}
message GetOrderRequest { string id = 1; }
message Order { string id = 1; double total = 2; OrderStatus status = 3; }
enum OrderStatus { DRAFT = 0; PAID = 1; SHIPPED = 2; }
2. Generating Service Stubs
| Language | Tool |
|---|---|
| Java | protoc --java_out --grpc-java_out or Gradle plugin |
| Go | protoc-gen-go + protoc-gen-go-grpc |
| Node/TS | @grpc/proto-loader or ts-proto |
| Python | grpcio-tools |
| Modern | buf generate with buf.gen.yaml |
3. Implementing Unary RPCs
Example: Unary server (Java)
public class OrderImpl extends OrderServiceGrpc.OrderServiceImplBase {
@Override public void get(GetOrderRequest req, StreamObserver<Order> out) {
Order o = repo.find(req.getId())
.orElseThrow(() -> Status.NOT_FOUND.withDescription("order").asRuntimeException());
out.onNext(o);
out.onCompleted();
}
}
| RPC Style | Pattern |
|---|---|
| Unary | 1 request → 1 response |
| When | CRUD, auth, simple lookups |
4. Implementing Server Streaming
| Use Case | Example |
|---|---|
| Large lists | Stream rows, bounded memory |
| Live feeds | Stock prices, telemetry |
| Backpressure | Use ServerCallStreamObserver.isReady() |
5. Implementing Client Streaming
| Use Case | Example |
|---|---|
| Bulk uploads | Stream events; one summary back |
| Aggregations | Compute over long input stream |
| Flow control | Server signals readiness |
6. Implementing Bidirectional Streaming
| Use Case | Example |
|---|---|
| Chat / collab | Independent send/recv |
| Real-time sync | State diff streams both ways |
| Order independence | Frames interleave freely |
7. Using gRPC Metadata
| Metadata Key | Use |
|---|---|
| authorization | Bearer token |
| x-request-id | Correlation ID |
| grpc-timeout | Auto-set from withDeadline |
| -bin suffix | Binary metadata: trace-bin |
8. Handling gRPC Errors
| Status Code | Maps To |
|---|---|
| OK (0) | Success |
| CANCELLED (1) | Client cancelled |
| INVALID_ARGUMENT (3) | HTTP 400 |
| DEADLINE_EXCEEDED (4) | HTTP 504 |
| NOT_FOUND (5) | HTTP 404 |
| ALREADY_EXISTS (6) | HTTP 409 |
| PERMISSION_DENIED (7) | HTTP 403 |
| RESOURCE_EXHAUSTED (8) | HTTP 429 |
| FAILED_PRECONDITION (9) | HTTP 412 |
| UNAUTHENTICATED (16) | HTTP 401 |
| UNAVAILABLE (14) | HTTP 503; safe to retry |
| INTERNAL (13) | HTTP 500 |
Note: Use
google.rpc.Status with ErrorInfo/BadRequest details for structured errors.9. Implementing Deadlines and Timeouts
| Concept | Detail |
|---|---|
| Deadline (absolute) | Set by client; propagates across services |
| Timeout (relative) | Client sugar: withDeadlineAfter(2, SECONDS) |
| Server check | Context.current().getDeadline() to abort early |
| Propagation | Reduces remaining budget down the call chain |
10. Using gRPC Interceptors
| Interceptor | Purpose |
|---|---|
| Auth | Validate token, populate Context |
| Logging | Method, latency, status |
| Tracing | OpenTelemetry spans |
| Metrics | Prometheus RED metrics |
| Retry/Hedging | Client-side |
11. Implementing Load Balancing
| Mode | Detail |
|---|---|
| pick_first | Default; one connection |
| round_robin | Distributes RPCs evenly across subchannels |
| xDS | Envoy/proxyless service mesh policies |
| Name resolver | DNS, Consul, Kubernetes headless service |
| Server-side | L7 proxy (Envoy) terminates HTTP/2 frames |
12. Managing Protocol Buffer Versioning
| Rule | Why |
|---|---|
| Never reuse field numbers | Wire format depends on tag |
| Don't change types | Decoders may misread bytes |
| Add new fields with defaults | Old clients ignore them |
reserved removed fields | Prevent accidental reuse |
| Version package | shop.v1, shop.v2 for breaking changes |
| Use Buf | buf breaking against last release |