Chapter 9: Channels, Connections, and Subchannels
A gRPC Channel is the client's view of a connection to a server. But it's much more than a raw TCP socket — it's a smart abstraction that handles connection pooling, load balancing, reconnection, and health checking.
The Channel Architecture
Channel States
A channel transitions through these states:
| State | Meaning | RPCs? |
|---|---|---|
| IDLE | No connection attempt yet (lazy connect) | Queued, triggers connect |
| CONNECTING | TCP/TLS handshake in progress | Queued |
| READY | Connected, RPCs can flow | Yes |
| TRANSIENT_FAILURE | Connection failed, will retry with backoff | Fail fast or queue |
| SHUTDOWN | Channel closed by user | Rejected |
Channel Creation
// C++ — Create a channel (this is CHEAP, do it once)
auto channel = grpc::CreateChannel(
"myservice.example.com:443",
grpc::SslCredentials(grpc::SslCredentialsOptions())
);
// Channel is LAZY — no connection until first RPC
// Channel is LONG-LIVED — reuse across your application's lifetime
// Channel is THREAD-SAFE — share across threads
// Create stub from channel
auto stub = MyService::NewStub(channel);
Do NOT create a new channel per RPC. Channels are expensive to set up (DNS resolution, TCP handshake, TLS handshake) but cheap to reuse. Create one channel at startup and share it. One channel can handle thousands of concurrent RPCs via HTTP/2 multiplexing.
Subchannels
A subchannel represents a connection to a single backend address. When a name resolves to multiple IPs, the channel creates one subchannel per address:
// Target: "dns:///myservice.prod:443"
// DNS resolves to: 10.0.1.1, 10.0.1.2, 10.0.1.3
// → Channel creates 3 subchannels
// → Load balancer picks which subchannel to use per RPC
Connection Keepalive
gRPC uses HTTP/2 PING frames to detect dead connections:
// C++ channel args for keepalive
grpc::ChannelArguments args;
// Send keepalive ping every 30 seconds if no activity
args.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, 30000);
// Wait 10 seconds for ping response before considering dead
args.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 10000);
// Send keepalive even with no active RPCs
args.SetInt(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1);
auto channel = grpc::CreateCustomChannel(target, creds, args);
In Kubernetes/cloud environments, load balancers and NAT gateways silently drop idle TCP connections (typically after 5-10 minutes). Without keepalive, your next RPC after idle time hits a dead connection and fails. Keepalive pings prevent this.
Channel vs Connection vs Stream
| Concept | Lifetime | Multiplicity |
|---|---|---|
| Channel | Application lifetime | 1 per target service |
| Subchannel | Until address removed | 1 per backend address |
| HTTP/2 Connection | Until failure/GOAWAY | 1 per subchannel |
| HTTP/2 Stream | Duration of one RPC | Many per connection (100s) |
- Channel = smart connection abstraction (resolver + LB + reconnection)
- Create once, reuse forever. Thread-safe. Don't create per-RPC.
- Channels are lazy — no connection until first RPC
- Subchannels = one TCP connection per backend address
- Configure keepalive to prevent silent connection drops in cloud environments