HTTP/2多路复用是否生效需验证单连接多流行为:curl -I --http2确认HTTP/2 200响应,Wireshark抓包观察同TCP流中多Stream ID HEADERS帧交错发送,压测对比HTTP/1.1建6连接与HTTP/2仅用1–2连接完成10并发请求。

HTTP/2开启后不能只看协议版本,得验证多路复用是否真生效
FrankenPHP 1.9 默认启用 HTTP/2,但仅当走 HTTPS 且 ALPN 协商成功时才实际使用 h2 —— 明文 HTTP(http://)永远 fallback 到 HTTP/1.1。光在浏览器 Network 标签里看到 Protocol 是 h2 不够,得确认“单连接承载多个流”这个核心行为是否发生:
• 用 curl -I --http2 -k https://localhost:8080 检查响应首行是否为 HTTP/2 200
• 抓包看同一 TCP 连接中是否有多个不同 Stream ID 的 HEADERS 帧交错发送
• 对比压测:相同域名并发 10 个请求,HTTP/1.1 通常建 6 个连接,HTTP/2 应稳定在 1–2 个连接内完成
压测工具必须支持 HTTP/2 并校验响应完整性
用 ab 或默认配置的 wrk 测 FrankenPHP 的 HTTP/2 性能会严重失真:
• ab 不支持 HTTP/2,它模拟的是 CGI 式逐请求启动,测出来的是“冷启动开销”,不是真实吞吐
• wrk 默认不校验响应体,遇到 Laravel 中间件返回的 500 或 JSON 格式错误会静默跳过,掩盖稳定性问题
• 推荐用 vegeta:vegeta attack -targets=targets.txt -rate=100 -duration=30s -http2 -include-body,再用 vegeta report -type=json 查 latencies.95 和 errors 字段
• 关键错误类型是 context deadline exceeded:说明 PHP worker 队列已满,不是网络或 TLS 问题
并发性能瓶颈往往不在网络层,而在 PHP worker 队列和下游依赖
HTTP/2 多路复用把请求全塞进少数连接里,反而会放大后端调度瓶颈:
• 访问 /metrics 端点(需 Caddy 启用 prometheus 指令),盯紧 frankenphp_queue_depth:持续 > 0 表示请求进不来,线程池已饱和
• 看 frankenphp_threads_busy / frankenphp_threads_total 比值,长期 > 0.8 就该调高 --workers 或检查阻塞点
• 数据库连接耗尽是高频原因:Symfony/Laravel 在 worker 模式下复用连接,但默认不持久化;执行 show status like 'Threads_connected' 对比 MySQL 的 max_connections,接近即为瓶颈
• 用 curl -w 拆解耗时:ttfb - tls 显著拉长 + CPU 低 = PHP 层排队或下游卡住,不是 HTTP/2 本身的问题
调参时必须锁死变量,否则 QPS 对比毫无意义
FrankenPHP 的 QPS 对配置极其敏感,HTTP/2 下尤甚:
• 每次压测前必须清空 OPcache:frankenphp php-cli -r "opcache_reset();",否则第二次结果虚高
• 固定 --workers、--max-requests、--env=APP_ENV=production,禁用文件监听(如 Laravel Octane 的 --watch)
• 路由必须明确走 PHP 执行路径,比如 /api/health,别压 /(可能命中 Caddy 静态服务)
• 先测纯 HTTP 再比 HTTPS:HTTP/3 + TLS 握手抖动会干扰 HTTP/2 多路复用的真实表现
立即学习“PHP免费学习笔记(深入)”;
HTTP/2 开启后,并发性能评估的核心不是“有没有协议”,而是“复用是否被充分利用”以及“后端能否跟上这种集中投递节奏”。FrankenPHP 的队列深度、worker 忙碌率和下游连接池状态,比 curl 输出的 HTTP/2 字样重要得多。



















