gRPC in Microservices & Load Balancing

gRPC — gRPC in Microservices & Load Balancing
gRPC
Chapter 8 · gRPC in Microservices & Load Balancing

🌐 gRPC in Microservices & Load Balancing

Chapter 7 secured calls between services. This chapter covers the operational reality of running those services as many instances at once — how a client finds a healthy instance at all, and how load actually gets spread across them.

Service Discovery

In a real microservices deployment, a service like order-service rarely runs as one fixed instance — it runs as many, scaling up and down, restarting, and being redeployed constantly. A client needs a way to find currently healthy instances rather than hardcoding one IP address that might not even exist anymore by the time a call is made. Service discovery solves this — a DNS record resolving to multiple IPs, or a dedicated registry (Consul, etcd, or Kubernetes' own built-in service discovery) that gRPC's resolver plugins can query directly.

Client-Side Load Balancing

With client-side load balancing, the gRPC client itself receives a list of available addresses (via service discovery) and decides which one to send each call to — round-robin is the simple default. This works especially well for gRPC specifically because of Chapter 1's HTTP/2 multiplexing: a client picks a backend connection once and reuses it for many calls, rather than a fresh choice being needed for every individual request the way a simpler, connectionless client model might behave.

// Conceptual: a DNS-based resolver feeding round-robin client-side LB const client = new OrderServiceClient( 'dns:///order-service.internal:50051', { loadBalancingPolicy: 'round_robin' } );

Proxy Load Balancing

The alternative: a dedicated proxy (an L7 proxy like Envoy, common in a service mesh) sits between client and servers. The client just talks to the proxy — it never needs to know about individual instances, service discovery, or balancing logic at all; the proxy handles all of it.

Client-Side vs. Proxy Load Balancing

Client-Side

The client is balancing-aware — it runs resolver logic and picks a backend itself. Simpler infrastructure, more logic embedded in every client.

Proxy

The client stays simple — a proxy (often part of a service mesh like Istio+Envoy) does all the routing and balancing work centrally.

ApproachWho Decides RoutingCommon Use
Client-side LBThe client itself, via a resolverSimpler deployments, fewer moving infrastructure pieces
Proxy LBA dedicated proxy/service meshLarger microservice fleets, centralized traffic policy

Client-Side LB Fits When

The deployment is simpler, and adding resolver/balancing logic to clients directly is an acceptable trade for not running extra infrastructure.

Proxy/Service Mesh Fits When

Many services need consistent routing, retry, and observability policy applied centrally, without embedding that logic separately into every client.

When gRPC Is the Right Internal-Service Choice vs. REST/GraphQL for Public APIs

This closes the loop back to Chapter 1's positioning: gRPC's performance and schema-first benefits pay off most within a trust boundary you control — internal services, secured with mTLS (Chapter 7), discovered and balanced across instances (this chapter). The moment an API needs to be called by a browser or an arbitrary third-party developer, Chapter 1's browser-support gotcha becomes decisive again: REST or GraphQL's universal compatibility wins for anything public-facing, while gRPC remains the stronger choice for the internal service-to-service traffic it was originally built for.

💻 Coding Challenges

Challenge 1: Explain Why Hardcoding an IP Fails

Explain why a gRPC client hardcoding one server IP address is a poor fit for a real microservices deployment, using this chapter's material.

Goal: Practice connecting the dynamic nature of scaled deployments to the need for service discovery.

→ Solution

Challenge 2: Client-Side or Proxy LB?

A small team runs three internal services with simple routing needs and wants to avoid operating extra infrastructure. A larger organization runs 200 microservices and wants centralized traffic policy and observability. Recommend an approach for each.

Goal: Practice matching organizational scale to the right load-balancing approach.

→ Solution

Challenge 3: Synthesize the Course's gRPC-vs-REST Thread

Using material from Chapters 1, 7, and this chapter, explain in a few sentences why gRPC is well suited to internal microservices but not to a public API a mobile app calls directly.

Goal: Practice pulling together multiple chapters' worth of reasoning into one coherent argument.

→ Solution

⚠️ Gotcha: Long-Lived Connections Can Leave New Instances Starved of Traffic

Chapter 1's HTTP/2 multiplexing is a real strength — but it has an operational cost here: because gRPC clients reuse one long-lived connection for many calls, a client that already picked a backend connection before a new instance was added to the pool has no reason to reconnect and discover it. In a naive setup, a freshly scaled-up instance can sit there receiving zero traffic while existing clients keep happily reusing their old connections to the original instances — the opposite of what adding capacity was supposed to achieve. Real deployments address this with periodic connection refresh/rebalancing (client-side) or by routing all traffic through a proxy (this chapter's proxy approach) that's aware of new instances immediately, rather than assuming clients will naturally redistribute themselves.

🎯 What's Next

The final chapter is the Capstone: Building a Small gRPC Service — a real two-service example (a user service plus an order service calling it) combining schema design, streaming, interceptors, and auth.