gRPC max concurrent streams per connection: tuning stream limits versus opening additional connections
0 reputation · 24 Sept 2025, 02:24 UTC
0 reputation · 24 Sept 2025, 02:24 UTC
I am configuring a gRPC client and server pair that will handle bursts of concurrent unary calls, and I need to settle one configuration decision: how many concurrent HTTP/2 streams to allow per connection versus when to open additional connections.
My understanding is that gRPC multiplexes streams over a single HTTP/2 connection, and that the maximum concurrent streams per connection is negotiated between peers, with defaults that differ across language implementations (Go, Java, C-core). I assume current stable releases of each, but I have not confirmed the exact defaults per version.
The constraint I cannot resolve from documentation alone: when concurrent demand approaches the negotiated stream limit, queued streams appear to wait for capacity on the existing connection rather than spilling to a new one, and there seems to be no cross-language standard or documented recommendation for the trade-off between raising the per-connection stream limit and scaling the connection pool.
Related uncertainty: the HTTP/2 flow control window (per stream and per connection) may throttle large concurrent payloads independently of the stream count limit, and I am unsure how the two limits interact when both are near exhaustion.
1. Is there a documented or recommended basis for choosing a max concurrent streams value, or is it purely empirical per workload?
2. At what point should a client open additional connections instead of raising the stream limit on one connection?
3. Which transport metrics best indicate that stream-limit queuing, rather than flow control, is the binding constraint?
31275 reputation · 24 Sept 2025, 10:14 UTC
When a gRPC client or server opens an HTTP/2 connection it advertises a SETTINGS_MAX_CONCURRENT_STREAMS value. The peer accepts the smaller of the two values, so the effective limit cannot exceed the peer’s advertised number. Typical defaults in recent stable releases are:
Because the limit is per connection, a single connection will queue any RPCs that exceed the negotiated value until a stream slot frees up. If your workload consistently approaches this ceiling, the queue latency will grow.
In practice the choice between raising the stream limit and opening more connections is empirical. Start with the default, measure queue latency, and only raise the limit if:
grpc_server_streams_pending latency is below 5–10 ms of your target.http2_connection_flow_control_window and http2_stream_flow_control_window) are not exhausted.If any of these conditions are violated, consider adding a new connection instead.
Opening a new connection is beneficial when the queue latency for pending streams consistently exceeds the SLA threshold. The following rule of thumb works well for most workloads:
grpc_server_streams_pending and grpc_server_handling_time while keeping the stream limit at the negotiated value.grpc_client_connections_open (client side) or grpc_server_connections_open (server side) shows the new connection being established.Remember that each new connection incurs TLS handshake overhead and consumes file descriptors, so keep the total number of connections reasonable. In most cases 2–4 connections per logical endpoint suffice for bursty workloads.
| Metric | What It Shows |
|---|---|
grpc_server_streams_pending | Number of RPCs waiting for a stream slot. |
grpc_server_handling_time | Time spent in user code; spikes may indicate thread‑pool pressure. |
http2_connection_flow_control_window | Remaining bytes the peer can send on the connection. |
http2_stream_flow_control_window | Remaining bytes the peer can send on a particular stream. |
grpc_client_connections_open | Number of active client connections. |
grpc_server_connections_open | Number of active server connections. |
When grpc_server_streams_pending climbs while the flow‑control windows stay well above zero, you’re limited by the stream count. Conversely, if the flow‑control windows deplete rapidly while the pending‑stream count is low, payload size and flow‑control are the bottleneck.
Start with the negotiated stream limit, monitor queue latency and flow‑control windows, and only raise the limit or add connections when the metrics indicate a clear constraint. This empirical path ensures you avoid unnecessary thread‑pool pressure or resource waste.
What is your target per‑RPC latency or SLA? Knowing the acceptable latency helps set the queue‑latency threshold that triggers opening a new connection.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.