Lesson 15 · Architecture & production
Load balancing & service discovery
Why the thing that balances REST silently breaks gRPC.
Your win: explain why gRPC load balancing is different from HTTP/1.1, name the two approaches, and connect it to streaming — a favourite "gotcha" interview question.
The problem: one connection carries everything
gRPC runs on a long-lived HTTP/2 connection that multiplexes many requests (Lesson 1). A naive connection-level (L4) load balancer picks a backend once, at connect time — so every request on that connection pins to that one backend. No per-request spreading.1
HTTP/1.1 opens a connection per request, so a plain LB balances by accident. gRPC's efficiency (connection reuse) is exactly what defeats that LB. You need something smarter.
Two approaches
Proxy LB (L7)
A gRPC-aware proxy (Envoy, a service mesh, a k8s L7 ingress) terminates HTTP/2, inspects each request, and balances per-RPC across backends. The client talks to the proxy only.
Client-side LB
The client discovers all backends (via service discovery — DNS, a registry, a k8s headless service) and round-robins across them per RPC itself. No extra hop.
rsc.GRPCDial("notificationmgmt"),
bootstrap/resource.go:351) with insecure in-cluster
creds. The balancing is provided by the platform (Kubernetes service / mesh)
rather than explicit client-side LB policy in the code — a common and pragmatic setup.
"gRPC on Kubernetes needs an L7-aware LB or headless-service client-side balancing" is
the interview-ready summary.
gRPC — "gRPC Load Balancing" + "gRPC on Kubernetes without tears"
The blog lays out proxy vs client-side LB precisely; the Kubernetes post shows the real-world fix for the connection-pinning problem.
→ grpc.io/blog/grpc-load-balancing
→ kubernetes.io — gRPC LB without tears
Check yourself (from memory)
Q1. A naive connection-level LB balances gRPC poorly because…
Q2. The two gRPC load-balancing approaches are…
Q3. A long-lived stream complicates LB because it…
grpc.WithDefaultServiceConfig)? Ask me.