HTTP/2 消除 HTTP 响应顺序阻塞
这里的“消除队头阻塞”,指解除同一条连接上的 HTTP 响应顺序约束,而不是消除 TCP 队头阻塞。较晚发起的响应可以在较早的处理函数仍然等待时完成,但共享的传输容量、连接级流控和应用瓶颈,依然可能把不同流的进度关联起来。先说明这一层边界,才能区分应用协议的顺序约束与底层传输等待。 分清两种排队 HTTP/1.1 本来就支持持久连接,并非一个请求必须新建一条连接。多个请求可以顺序复用套接字;流水线还允许同时存在多个未完成请求,服务器也可以并行处理安全方法,但同一条连接上的响应必须按照请求顺序发送。因此,第一份响应过慢,会延迟后续响应的交付。客户端经常通过多开连接缓解这个限制。 HTTP/2 给帧关联流标识,在同一条连接上交错传递消息。服务器可以发送已经准备好的响应,不必先完成另一份无关响应。这是 HTTP 层的独立推进能力,不代表每个处理函数获得独占处理器、数据库、带宽或无限调度机会,也不意味着应用的共享资源自动变成互不影响。 TCP 仍然提供一个有序字节流。某段字节缺失时,后面已经收到的字节可能必须等待补齐缺口,才能交付应用,其中也可能包含其他 HTTP/2 流的帧。此前已经交付的字节并不会被收回。QUIC 则在每条流内保持有序,不要求不同流之间统一排序;不能把它解释成普通流数据可以任意乱序交付,也不能据此忽略共享的拥塞控制和流控预算。 并发仍然受到预算约束 对端通过 SETTINGS_MAX_CONCURRENT_STREAMS 限制活动流数量。DATA 帧还同时消耗流级和连接级的流控额度。每条流拥有单独窗口,不等于无限独立:连接额度耗尽时,其他流的 DATA 也可能停住。接收方如何消费正文、何时更新窗口,以及共享工作池、锁和上游连接是否紧张,都会改变实际推进情况。 应检查所测那一跳真正协商出的协议。浏览器到代理使用 HTTP/2,不能证明代理到源站也是如此;TLS 握手成功也不足以证明使用了 HTTP/2。Go 配置自定义 TLS 参数后,可能需要 ForceAttemptHTTP2 才会尝试 HTTP/2,但最终证据仍是响应协议和协商出的 h2,而不是配置字段已经存在。 坏掉的样子 快请求先完成,可能只是客户端另开了一条 TCP 连接;单连接 HTTP/1.1 客户端则可能在请求抵达服务器之前,就已经让它等待。应同时核对服务端接受的连接数量和客户端获取连接的事件,再判断排队发生在哪里。仅凭两条访问日志的结束时间,无法区分这些情况。 增加流并发量,不能修复过载数据库,也不能修复未读数据造成的共享窗口紧张。应记录正文完成情况、活动流、共享资源等待以及逐跳协议。不要把每次慢请求都称为 TCP 队头阻塞,更不要把升级协议直接写成已经实测的页面提速;性能结论需要独立的负载与网络测量支持。 ...