gRPC Architecture
Build high-performance microservice communication with gRPC. Covers Protocol Buffers, service definitions, streaming patterns, error handling, load balancing, and the patterns that make gRPC the standard for internal service communication.
gRPC is a high-performance RPC framework that uses Protocol Buffers for serialization and HTTP/2 for transport. Compared to REST/JSON, gRPC is 2-10x faster, generates type-safe client and server code, supports streaming, and produces payloads 3-5x smaller. It is the standard for internal microservice communication at Google, Netflix, and most cloud-native companies.
gRPC vs REST
REST/JSON:
Transport: HTTP/1.1 (text-based)
Serialization: JSON (text, human readable)
Contract: OpenAPI/Swagger (optional, not enforced)
Streaming: Not native (WebSocket is separate)
Code generation: Optional
gRPC:
Transport: HTTP/2 (binary, multiplexed)
Serialization: Protocol Buffers (binary, compact)
Contract: .proto file (required, enforced)
Streaming: Native (unary, server, client, bidirectional)
Code generation: Automatic (10+ languages)
Performance comparison:
Payload size: gRPC 3-5x smaller than JSON
Latency: gRPC 2-10x lower
Throughput: gRPC 3-10x higher
CPU usage: gRPC 30-50% lower
Protocol Buffers
// user_service.proto
syntax = "proto3";
package userservice;
// Service definition
service UserService {
// Unary RPC
rpc GetUser(GetUserRequest) returns (User);
rpc CreateUser(CreateUserRequest) returns (User);
rpc UpdateUser(UpdateUserRequest) returns (User);
rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
// Server streaming: stream many users
rpc ListUsers(ListUsersRequest) returns (stream User);
// Bidirectional streaming
rpc SyncUsers(stream UserUpdate) returns (stream SyncResult);
}
// Message definitions
message User {
string id = 1;
string email = 2;
string name = 3;
UserPlan plan = 4;
google.protobuf.Timestamp created_at = 5;
}
message GetUserRequest {
string id = 1;
}
message CreateUserRequest {
string email = 1;
string name = 2;
UserPlan plan = 3;
}
enum UserPlan {
USER_PLAN_UNSPECIFIED = 0;
USER_PLAN_FREE = 1;
USER_PLAN_PRO = 2;
USER_PLAN_ENTERPRISE = 3;
}
Server Implementation (Python)
import grpc
from concurrent import futures
import user_service_pb2 as pb2
import user_service_pb2_grpc as pb2_grpc
class UserServiceServicer(pb2_grpc.UserServiceServicer):
def GetUser(self, request, context):
user = self.db.get_user(request.id)
if not user:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details(f"User {request.id} not found")
return pb2.User()
return pb2.User(
id=user.id,
email=user.email,
name=user.name,
plan=user.plan,
)
def ListUsers(self, request, context):
"""Server streaming: yield users one at a time."""
users = self.db.list_users(
page_size=request.page_size,
filter=request.filter,
)
for user in users:
yield pb2.User(
id=user.id,
email=user.email,
name=user.name,
)
# Start server
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
pb2_grpc.add_UserServiceServicer_to_server(UserServiceServicer(), server)
server.add_insecure_port("[::]:50051")
server.start()
Streaming Patterns
Unary (Request → Response):
Client sends one message, server returns one message
Like REST but with Protobuf
Use for: CRUD operations
Server Streaming (Request → Stream of Responses):
Client sends one message, server returns a stream
Use for: List operations, real-time feeds, large result sets
Client Streaming (Stream of Requests → Response):
Client sends a stream, server returns one message
Use for: File upload, batch operations, sensor data
Bidirectional Streaming (Stream ↔ Stream):
Both client and server send streams simultaneously
Use for: Chat, real-time collaboration, game state sync
Anti-Patterns
| Anti-Pattern | Consequence | Fix |
|---|---|---|
| gRPC for public APIs | Browsers can’t call gRPC natively | REST/GraphQL for public, gRPC for internal |
| No deadlines/timeouts | Requests hang forever | Always set deadline on every call |
| Large messages (>4MB) | gRPC has 4MB default limit | Streaming for large payloads |
| No error codes | Cannot handle errors properly | Use gRPC status codes (NOT_FOUND, etc.) |
| Breaking proto changes | Clients crash | Never remove fields, use reserved |
gRPC is the right choice for internal service communication where performance matters. For public APIs that browsers consume, use REST or GraphQL with a gRPC gateway.