http2_max_concurrent_streams 限制单个 HTTP/2 连接的并发流数,默认128,需 Nginx≥1.9.5、启用 HTTPS 并配置 listen ssl http2,它不控连接总数,只管每连接内最多同时处理几个请求,过高易压垮后端,过低则浪费多路复用优势。

这个值设太高,后端容易被单个连接压垮;设太低,又浪费 HTTP/2 多路复用优势。它不控制总连接数,只管“一个连接里能同时跑几个请求”。
为什么 http2_max_concurrent_streams 会吃内存?
每个活跃流(stream)在 Nginx 内部要维护独立的缓冲区、HPACK 解压上下文、流窗口状态和优先级树节点。这些结构不是共享的——哪怕两个流请求的是同一张图片,它们的内存开销也各自计算。尤其当客户端开启大量并发 fetch 或长轮询时,http2_max_concurrent_streams 设得高,就等于允许单连接创建更多这类对象。
更隐蔽的问题是:http2_streams_index_size 默认 32,如果实际活跃流常达 80+,哈希冲突上升,查找开销变大,CPU 使用率可能悄悄升高,而你只盯着内存看。
- 默认 128 → 单连接典型内存占用约 1–2 MB(含缓冲区+上下文)
- 设为 256 → 不代表吞吐翻倍,但内存可能涨 40%~60%,尤其在万级连接时
- PHP-FPM 进程池只有 32 个 worker?那
http2_max_concurrent_streams超过 40 就大概率触发上游排队,反而增加延迟
怎么判断当前值是否合理?
别猜,看真实负载下的三个信号:
第一,用 nginx -V 确认已启用 http_v2_module;第二,在 server 块里加 access_log /var/log/nginx/h2.log http2(自定义日志格式需含 $http2 和 $request_length);第三,压测或高峰时段抽样检查:
- 查
curl -I --http2 https://yoursite.com | grep HTTP,确认响应头确实是HTTP/2 200 - 用
ss -s看 ESTAB 连接数,再对比nginx_stub_status的 Active connections,如果前者远大于后者,说明很多连接没走 HTTP/2(可能是 TLS 握手失败或 ALPN 协商降级) - 观察
upstream_addr日志中,单个 client IP 对应的 upstream 连接是否频繁新建——如果是,说明http2_max_concurrent_streams太小,流被快速耗尽,被迫重建连接
常见业务场景的推荐取值
没有银弹,但有锚点:
- API 服务(JSON 接口为主,后端是 Go/Java):设为 64~100。这类请求处理快,但上游连接池通常较紧,避免单连接打满线程
- 大图/静态资源站(前端大量
<img>并发加载):可设为 200~256。前提是worker_connections≥ 16384,且后端是 CDN 或对象存储,无连接瓶颈 - 含 SSE 或 WebSocket 升级的页面(如实时通知):必须设为 ≤ 32。因为每个 SSE 流长期占用一个 stream ID,不释放,高值会导致连接很快达到上限并断开
- 混合型 CMS(PHP + 静态资源):建议 80。配合
proxy_buffering off和upstream keepalive 16,平衡前后端压力
容易被忽略的协同配置
单独调 http2_max_concurrent_streams 效果有限,它必须和三个参数咬合:
-
http2_max_requests:设为 500~800。防止某个连接跑了上千请求后内存泄漏累积(尤其 PHP 场景),比单纯靠超时更可控 -
http2_idle_timeout:设为 180s(3 分钟)。比默认 300s 更激进一点,让空闲连接更快释放流索引表和 HPACK 上下文 -
upstream keepalive:必须显式设置,比如keepalive 32。否则即使客户端复用连接,Nginx 每次仍可能新建后端连接,放大上游压力
真正卡脖子的往往不是这个值本身,而是它和上游连接池、worker 进程资源、甚至客户端重试逻辑之间的错位——调参前,先确认你的 upstream 是不是真在复用连接。



















