代理服务支持 HTTP/2 的方式是充当中间层:既可以终结 HTTP/2 连接并以 HTTP/1.1 将请求转发给源站,也可以在客户端与源站之间端到端地透传 HTTP/2。
HTTP/2 是 HTTP 协议的一次重大修订,旨在解决 HTTP/1.1 的固有局限,从而提升 Web 性能。其关键特性包括请求与响应的完全多路复用、通过 HPACK 进行的头部压缩、服务端推送(server push)以及请求优先级。对代理服务而言,管理这些特性需要特定的架构考量,以便在保持兼容性和可控性的同时充分利用 HTTP/2 的优势。
HTTP/2 协议基础
HTTP/2 在单条 TCP 连接上运行,通过建立多条双向 stream 来并发收发请求与响应。这种多路复用消除了 HTTP/1.1 中存在的队头阻塞(head-of-line blocking)。头部压缩(HPACK)将 HTTP 头部编码为紧凑的二进制格式并维护共享动态表,从而降低开销。服务端推送允许服务器主动向客户端发送它预计会被用到的资源,以降低延迟。
这些特性虽然有益,但对传统上在独立 TCP 连接上采用请求—响应模型的代理服务来说,也带来了复杂性。
面向 HTTP/2 的代理架构
代理服务通常采用两种主要架构之一来支持 HTTP/2:终结式或端到端式。
HTTP/2 终结(前端 HTTP/2,后端 HTTP/1.1)
在该模型中,代理服务器与客户端建立 HTTP/2 连接。它终结 HTTP/2 协议、解码请求,然后使用 HTTP/1.1 将请求转发给源站服务器。源站返回的响应(HTTP/1.1)随后被转码回 HTTP/2 并发送给客户端。
处理流程:
- 客户端向代理发起 HTTP/2 连接(通常基于 TLS,由 ALPN 协商出
h2)。 - 代理接收 HTTP/2 帧,重建类似 HTTP/1.1 的请求(包括解码 HPACK 头部)。
- 代理将 HTTP/1.1 请求转发给源站服务器。
- 源站服务器以 HTTP/1.1 响应。
- 代理接收 HTTP/1.1 响应,将其编码为 HTTP/2 帧(包括 HPACK 压缩),并发送给客户端。
优点:
- 后端兼容性: 源站服务器无需支持 HTTP/2。这对遗留系统或尚未升级的服务很有帮助。
- 卸载: 代理承担 HTTP/2 协议协商、头部压缩/解压和 stream 管理的计算开销,减轻源站服务器的负载。
- 可控性: 更容易执行缓存、负载均衡、请求改写和安全过滤等传统代理功能,这些功能通常是按 HTTP/1.1 语义设计的。
缺点:
- 特性丢失: 源站发起的服务端推送等 HTTP/2 特性无法直接透传。代理需要自行实现服务端推送逻辑。
- 协议不匹配的开销: HTTP/2 与 HTTP/1.1 之间的持续转码会在代理端带来处理开销。
- 延迟增加: 与直连的 HTTP/2 连接相比,这一额外处理步骤会引入轻微延迟。
适用场景: 适合希望在客户端侧获得 HTTP/2 性能收益、但后端基础设施尚未支持 HTTP/2 的场景,或需要在代理侧对请求进行大量改写的情况。
端到端 HTTP/2(完全代理)
在端到端 HTTP/2 代理架构中,代理同时与客户端和源站服务器保持 HTTP/2 连接。代理充当透明中间层,直接转发 HTTP/2 帧或 stream,不做协议转换。
处理流程:
- 客户端向代理发起 HTTP/2 连接。
- 代理与源站服务器建立 HTTP/2 连接。
- 代理在客户端与源站之间转发 HTTP/2 帧/stream。
- 代理管理 stream ID,并可能对 stream 进行优先级排序。
优点:
- 完整保留特性: 包括源站的服务端推送、stream 优先级和 HPACK 压缩在内的所有 HTTP/2 特性都能端到端保留。
- 更低延迟: 免除转码开销;如果源站服务器在地理上较近或针对 HTTP/2 做了高度优化,延迟可能更低。
- 代理开销更小: 代理的职责主要是转发帧和管理 stream,而非完整的协议转换。
缺点:
- 对源站服务器的要求: 需要源站服务器完整支持 HTTP/2。
- 可见性/可控性降低: 由于 HPACK 压缩和 HTTP/2 帧的二进制特性,代理直接修改 HTTP 头部或请求体变得更复杂。深度包检测通常需要完整解码并重新编码帧。
- 安全影响: 如果与源站的连接同样使用 TLS,代理将作为 TLS 终结点并向源站重新建立一条新的 TLS 连接,这可能影响安全态势或需要证书管理。
适用场景: 最适合客户端与源站服务器都支持 HTTP/2,且目标是在整条连接路径上最大化 HTTP/2 性能收益、除路由和负载均衡外尽量减少代理干预的情况。
代理服务的关键考量
应用层协议协商(ALPN)
HTTP/2 通常通过 TLS 使用 ALPN 协商。客户端连接代理时,会在 TLS ClientHello 消息中发送 ALPN 扩展,表明支持 h2(基于 TLS 的 HTTP/2)和 http/1.1。代理从中选择优先使用的协议。
示例(概念性 ALPN 握手):
ClientHello (ALPN: [h2, http/1.1]) -> Proxy
Proxy selects h2
ServerHello (ALPN: h2) <- Proxy
头部转换与 HPACK
在 HTTP/2 与 HTTP/1.1 之间转换时(终结模型),代理必须解压 HTTP/2 请求中的 HPACK 头部,并将 HTTP/1.1 头部重新编码为 HPACK 用于 HTTP/2 响应。这需要管理带状态的 HPACK 动态表。
stream 管理与优先级
HTTP/2 允许在单条连接上并发多条 stream。代理必须管理这些 stream,将其映射到后端连接(针对 HTTP/1.1 后端),或在遵循优先级提示的前提下转发它们。stream 管理不当会抵消 HTTP/2 的多路复用优势。
服务端推送的处理
- 终结式: 如果代理终结 HTTP/2,就无法直接转发源站发起的服务端推送请求。代理可以基于内容分析或配置实现自己的服务端推送逻辑。
- 端到端式: 在端到端架构中,源站的服务端推送帧会直接转发给客户端。
流量检测与改写
检测或改写 HTTP/2 流量比 HTTP/1.1 更困难。
* 加密: HTTP/2 几乎总是与 TLS 一起使用,代理必须终结 TLS 才能进行检测。
* 头部压缩: 修改头部需要用 HPACK 进行解码、修改并重新编码,这一过程带状态且较为复杂。
负载均衡
在 HTTP/2 下,同一客户端的多个请求可能通过一条连接到达。负载均衡器必须决定是把单条客户端连接上的所有 stream 都路由到同一台后端服务器(会话粘性),还是把各条 stream 分发到多台后端服务器。后者需要更复杂的、可感知 stream 的负载均衡。
配置示例(Nginx)
Nginx 作为常见的反向代理,同时支持 HTTP/2 终结和端到端代理。
HTTP/2 终结(前端 HTTP/2,后端 HTTP/1.1):
server {
listen 443 ssl http2; # 为客户端连接启用 HTTP/2
server_name example.com;
ssl_certificate /etc/nginx/certs/example.com.crt;
ssl_certificate_key /etc/nginx/certs/example.com.key;
location / {
proxy_pass http://backend_servers; # 后端为 HTTP/1.1
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
# Nginx 会自动把客户端的 HTTP/2 请求转换为发往后端的 HTTP/1.1
}
}
upstream backend_servers {
server 192.168.1.100:80;
server 192.168.1.101:80;
}
端到端 HTTP/2(前端 HTTP/2,后端 HTTP/2):
server {
listen 443 ssl http2; # 为客户端连接启用 HTTP/2
server_name example.com;
ssl_certificate /etc/nginx/certs/example.com.crt;
ssl_certificate_key /etc/nginx/certs/example.com.key;
location / {
proxy_pass https://backend_h2_servers; # 后端为 HTTP/2
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_ssl_server_name on; # 将 SNI 传递给后端
proxy_http_version 2; # 指示 Nginx 对后端连接使用 HTTP/2
}
}
upstream backend_h2_servers {
server 192.168.1.100:443;
server 192.168.1.101:443;
}
对比:HTTP/2 终结与端到端
| 特性 | HTTP/2 终结(前端 H2,后端 H1.1) | 端到端 HTTP/2(前端 H2,后端 H2) |
|---|---|---|
| 源站服务器支持 | 仅需 HTTP/1.1 | 必须支持 HTTP/2 |
| 协议转换 | 是(H2 <-> H1.1) | 否(H2 <-> H2) |
| 服务端推送 | 仅代理自行生成 | 源站生成的可透传 |
| 头部压缩 | 由代理进行 HPACK 编解码 | 代理转发已压缩的头部 |
| 性能 | 良好,但有转码开销 | 可能更优,代理开销更低 |
| 代理可控性 | 高(易于改写/检测) | 较低(改写/检测更复杂) |
| 复杂度 | 中等 | 中等到高(需管理后端 H2) |
| 延迟 | 因转码略高 | 可能更低 |
实际影响
在代理服务中实现 HTTP/2 会直接影响用户体验和基础设施效率。得益于多路复用和头部压缩,用户可获得更快的页面加载速度和更流畅的 Web 体验。对代理基础设施而言,支持 HTTP/2 需要谨慎的资源管理,尤其是 TLS 终结和 HPACK 处理带来的 CPU 占用。终结式与端到端式之间的选择,取决于现有的后端架构、性能目标以及在代理层控制流量的需求。安全始终至关重要:TLS 是绝大多数 HTTP/2 部署的前提,需要稳健的证书管理和安全的配置。
