HTTP/3 基于 UDP 和 QUIC,它把传输层从 TCP 转移到用户态协议栈,这让 Apache Traffic Server(ATS)这类传统代理缓存面临不小的改动。ATS 的缓存引擎、会话管理、插件接口原本都假定底层是 TCP,而 Grasshopper 这个 QUIC 实现模块正是为了把 HTTP/3 接入 ATS 而出现的。它并不是另起炉灶,而是通过 ATS 的 iocore 网络框架嵌入 QUIC 能力,让已有缓存逻辑继续复用。

为什么 Apache 代理缓存接入 HTTP/3 需要 Grasshopper
ATS 的工作流从 accept 一条 TCP 连接开始,经过 TLS 终止、HTTP 解析、缓存查找、源站回源、响应存储,每一步都依赖 TCP 的有序字节流和连接语义。HTTP/3 则完全不同:一个 QUIC 连接内可以并发多条流,每条流独立处理一个请求,丢包不会阻塞其他流。这意味着代理层必须实现流的调度与取消,还要处理连接迁移、路径 MTU 发现、0-RTT 早到数据等新概念。若在 ATS 内核里直接改 TCP 路径,风险很高。
Grasshopper 的价值在于把这些 QUIC 特性封装成事件和会话对象,再通过 ATS 的 Continuation 机制抛出回调。对缓存模块来说,收到的仍然是完整的请求头和请求体,只是数据来源从 TCP 缓冲变成 QUIC 流。Grasshopper 内部使用 UDP socket 收发报文,完成 QUIC 握手、加密帧解密、流重组和 QPACK 头部解压,最后把 HTTP/3 请求还原成 HTTP 语义。这样做的好处是,缓存命中、失效、层级回源等核心逻辑不需要为 QUIC 重写。
此外,ATS 作为边缘缓存,通常会部署在 CDN 节点上,运行在 443 端口。启用 Grasshopper 后,同一个端口可以同时接收 TCP 和 UDP 流量,通过 ALPN 协商 h3,实现旧客户端回退到 HTTP/2 或 HTTP/1.1。这种共存能力对生产灰度非常关键。
Grasshopper 如何与 ATS 缓存流水线协同
从架构上看,Grasshopper 分为 QUIC 引擎、HTTP/3 编解码层和 ATS 适配层三部分。QUIC 引擎维护 UDP 监听、连接 ID、加密上下文、丢包恢复与拥塞控制。HTTP/3 编解码层负责把每个请求/响应对映射到 QUIC 流,并用 QPACK 压缩头部。ATS 适配层则把 QUIC 流事件翻译成 TSVConn 和 TSIOBuffer 操作,让插件和缓存逻辑像处理 TCP 连接一样工作。
一个典型的请求路径如下:客户端通过 UDP 443 端口发起 QUIC 握手,TLS 1.3 完成后 ALPN 协商为 h3;随后客户端在一条双向流上发送请求头,Grasshopper 解压后交给 ATS 的 HttpSM 状态机;HttpSM 先生成缓存 key,再查询缓存。若命中缓存,则通过同一条 QUIC 流返回响应头与响应体;若未命中,ATS 回源获取内容,并在写入缓存的同时返回给客户端。整个过程中,传统缓存查找逻辑保持不变,只替换了传输承载。
下面这段 ATS 插件伪代码展示了如何拿到 QUIC 请求并生成缓存 key,逻辑与 TCP 场景几乎一致:
int
QuicRequestHandler(TSCont contp, TSEvent event, void *edata)
{
TSVConn vconn = reinterpret_cast<TSVConn>(edata);
TSMBuffer bufp = nullptr;
TSMLoc hdr_loc = nullptr;
TSIOBufferReader reader = TSIOBufferReaderAlloc();
// 读取QUIC流上的请求头
TSIOBufferReaderCopy(reader, bufp, hdr_loc);
int url_len = 0;
const char *url = TSUrlStringGet(bufp, hdr_loc, &url_len);
TSCacheUrlSet(bufp, hdr_loc, url, url_len);
// 交给缓存状态机处理
TSHttpTxn txnp = reinterpret_cast<TSHttpTxn>(vconn);
TSHttpTxnCacheableSet(txnp, 1);
TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
return 0;
}
注意这里用到的 TSCacheUrlSet 和 TSHttpTxnCacheableSet 都是 ATS 缓存 API,不关心底层是 TCP 还是 QUIC。Grasshopper 已经把传输差异屏蔽掉,因此旧插件也能以较低成本迁移到 HTTP/3。
缓存策略还需要考虑 QPACK 动态表对 CPU 的影响。HTTP/3 头部压缩不再使用 HPACK,而是 QPACK,它能避免队头阻塞,但需要维护动态表状态。如果边缘节点 CPU 紧张,可以适当降低 QPACK 动态表容量,减少压缩与解压开销。Grasshopper 通常提供 qpack_max_table_capacity 之类的参数,允许按节点负载调整。
编译配置与抓包验证步骤
要让 Grasshopper 跑起来,首先要编译 ATS 并开启 QUIC 支持。以源码构建为例,在 CMake 阶段打开 ENABLE_QUIC,并指定 TLS 库。Grasshopper 通常依赖 BoringSSL 或 QuicTLS,因为它们暴露了 QUIC 所需的 TLS 回调接口,而系统自带的 OpenSSL 可能不包含这些 API。
cmake -B build \ -DENABLE_QUIC=ON \ -DOPENSSL_INCLUDE_DIR=/opt/quictls/include \ -DOPENSSL_SSL_LIBRARY=/opt/quictls/lib/libssl.so \ -DOPENSSL_CRYPTO_LIBRARY=/opt/quictls/lib/libcrypto.so \ -DCMAKE_INSTALL_PREFIX=/opt/ats cmake --build build -j$(nproc) sudo cmake --install build
安装完成后,在 records.yaml 中配置 UDP 监听端口。ATS 支持在同一个 IP 上同时配置 TCP 和 QUIC 监听,例如把 443 端口同时用于 TCP 和 UDP,客户端会根据 ALPN 自行选择协议。这里给出一个最小化配置示例:
proxy.config.http.server_ports: 80 443:ssl 443:quic proxy.config.quic.server_ports: 443 proxy.config.http3.enabled: 1 proxy.config.quic.log_bin: /var/log/trafficserver/quic.log proxy.config.quic.qpack_max_table_capacity: 4096
重启 ATS 后,可以用 curl 直接验证 HTTP/3。较新版本的 curl 使用 --http3-only 参数,需要确保 curl 编译时带 HTTP/3 支持。若抓包则使用 tcpdump 观察 UDP 443 端口,确认 QUIC 初始包和握手包正常。命令如下:
curl --http3-only -I https://ipipp.com/ sudo tcpdump -i eth0 udp port 443 -w quic.pcap
如果握手失败,先检查 UDP 负载均衡是否把同一连接的五元组哈希到同一台节点。QUIC 连接 ID 虽然支持连接迁移,但传统四层负载均衡可能按 UDP 五元组分发,导致后续报文被转发到不同后端。可以在 LB 上启用基于连接 ID 的哈希,或先使用单节点测试。
另一个常见问题是 MTU。QUIC 对 IP 分片不友好,建议节点网络路径的 MTU 至少为 1500,并在防火墙上放行 UDP 443。若客户端路径存在小于 1500 的链路,可以开启 PMTUD,或降低 QUIC 的 max_packet_size。
HTTP/3 缓存命中率与性能调优
HTTP/3 的 0-RTT 特性会带来一个缓存层面的副作用:客户端可能在服务器尚未完全处理完首次请求时,就使用早到数据发起新请求。如果缓存 key 不包含连接相关状态,0-RTT 请求很可能直接命中缓存,这本身没有问题;但如果 0-RTT 数据携带了敏感操作,代理需要谨慎处理。Grasshopper 允许配置 0-RTT 策略,比如只接受 GET 请求的早到数据,禁止 POST 等非幂等操作走 0-RTT。
从性能上看,HTTP/3 的收益主要来自握手节省和队头阻塞消除。对于高丢包网络,QUIC 的多流独立重传能显著减少缓存响应的等待时间。但在网络质量较好的内网环境,HTTP/3 的 CPU 开销可能高于 HTTP/2,因为 UDP 收发包需要更多的用户态处理。建议先在小流量节点启用,观察 QPACK 解压 CPU、QUIC 连接数和 UDP 丢包率,再逐步扩大灰度。
还可以结合 ATS 的缓存分层能力,把热点对象预先推送到边缘,同时用 Grasshopper 提供的流优先级调度,让大文件下载不会阻塞小对象响应。Grasshopper 支持按流发送优先级调整,在代理缓存场景中可以优先发送 HTML 文档和 CSS/JS,再发送图片和视频,提升首屏体验。
最终落地时,不要把 HTTP/3 视为替代 HTTP/2 的银弹。对于已有 TCP 优化较好的链路,HTTP/3 的提升可能有限。Grasshopper 更适合那些需要减少握手 RTT、改善弱网性能、以及利用连接迁移的移动端场景。把 HTTP/3 作为并行通道灰度上线,才是 Apache 代理缓存升级的正确姿势。
Apache代理缓存HTTP/3Grasshopper QUIC修改时间:2026-09-26 10:08:50