HeadlinesBriefing HeadlinesBriefing.com

Go 中更好的 HTTP/1 连接复用

Hacker News •
×

请看这个函数,它向某个 URL 发送 GET 请求并返回 HTTP 状态码。使用 HTTP/1(GODEBUG=http2client=0),在作者的机器上于 Go 1.26 下对 index.golang.org 调用 100 次大约需要 710 毫秒。而在 Go 1.27 上,同样的程序大约只需 150 毫秒,快了近 5 倍。

差距来自建立连接的开销。通过新连接发送请求需要一次 TCP 握手(1 个往返时间,即 RTT)、一次 TLS 握手(在 TLS 1.2 下为 2 个 RTT),然后才是 HTTP 请求本身,总计约 3 个 RTT。在跨地区通信中,单向延迟往往达到或超过 50 毫秒,因此仅建立连接就可能耗费约 300 毫秒。即使是 index.golang.org 这样使用 TLS 1.3 且延迟相对较低的站点,建立新连接的开销依然非常明显。

为了避免这一开销,HTTP 客户端和服务器会尝试复用连接。HTTP/1.0 的客户端和服务器需要通过 Connection: keep-alive 请求头主动启用,而 HTTP/1.1 连接默认就是持久连接。HTTP/2 则可以在单个 TCP 连接上复用多个请求和响应。

不过,HTTP/1 中的连接复用可能失败。同一连接上的响应是按顺序发送的,因此 net/http 只有在当前响应体被完整读取之后,才能定位下一个响应的起始位置。在上面的示例中,响应体从未被读取,所以调用 resp.Body.Close 时,net/http 发现响应体未读完,无法安全地复用该连接。直到 Go 1.26,这都会导致每个请求都建立一个新连接。

来源: Hacker News · 由HeadlinesBriefing整理摘要