Loading

Go 标准库 HTTP2 协议升级细节

客户端行为

首次调用 Transport.RoundTrip()

  1. 初始化 Transport 的 TLS ALPN 列表,加入 "h2" ALPN (code)。同时设置 "h2" ALPN 对应的协议升级函数 (code)。

  2. 创建TCP连接,随即启动TLS握手流程 (code)。客户端发送给服务端的 TLS 握手消息,会携带步骤1中设置的 "h2" ALPN (code),与服务端协商。

  3. 服务端接受 "h2" ALPN,同样在握手消息中携带 "h2" 作为 ALPN 协商的结果,响应给客户端。客户端缓存此结果 (code)。

  4. 客户端根据 ALPN 协商结果——"h2",找回步骤1中设置的协议升级函数,通过此函数生成新的 HTTP2 Transport ,将其保存到连接的 alt 变量中 (code)。

  5. 因为 HTTP2 连接可以同时被多个请求复用(通过 HTTP2 Stream),没有被占用的问题,直接把它当成是“空闲”连接,放入到连接池中(code)。

  6. 由于连接的 alt 变量(HTTP2 Transport )不为空,客户端直接使用此 Transport 发送 http 请求,绕过了 HTTP1 相关的处理 (code)。

  7. HTTP2 Transport 创建一个新的 Stream 发送请求 (code)。

后续调用 Transport.RoundTrip()

  1. 客户端在连接池中发现“空闲”状态的 HTTP2 连接,因为 HTTP2 连接可以同时被多个请求复用(通过 HTTP2 Stream),没有被占用的问题。直接使用这个连接,而且无需将它从连接池中移除(仍旧保持“空闲”状态)。(code)

  2. 由于连接的 alt 变量(HTTP2 Transport )不为空,客户端直接使用此 Transport 发送 http 请求,绕过了 HTTP1 相关的处理 (code)。

  3. HTTP2 Transport 创建一个新的 Stream 发送请求 (code)。

服务端处理

  1. 服务端启动时,会初始化所支持的 TLS ALPN 列表,加入 "h2" ALPN (code)。同时设置 "h2" ALPN 对应的协议升级函数 (code)。
  2. 接受TCP连接,开始TLS握手流程(code)。不出意外,客户端会在 TLS 握手消息中提供 "h2" ALPN,服务端根据步骤1提供的所支持的 TLS ALPN 列表,最终 "h2" 会被选择为协商的结果。服务端缓存此结果(code)。
  3. 服务端基于 ALPN 协商结果——"h2",找回步骤1中设置的协议升级函数,直接通过此函数处理 HTTP 请求,绕过了 HTTP1 相关的处理 (code)。
  4. 处理 HTTP2 Stream (code)。

结论

  • Go 标准库通过 TLS (HTTPS) 的 ALPN 协商 直接识别处理 HTTP2 流量,没有走常规 HTTP1 升级 HTTP2 的流程,缩短了连接建立的时间,性能更好。
  • Transport 连接池托管 HTTP2 连接的意义不大,对于同一个域名,连接池内通常只有一个 HTTP2 连接,通过在 HTTP2 连接内 创建多个 Stream 来复用连接。
posted @ 2021-11-06 15:35  roy2220  阅读(549)  评论(0编辑  收藏  举报