在移动网络环境里,Android客户端通过传统TCP访问后端接口经常受队头阻塞和连接重建耗时影响。QUIC协议把传输层搬到UDP上,内置加密与多路复用,显著改善弱网体验。不过让业务源站全面支持HTTP/3成本较高,更务实的做法是在边缘用Apache承担代理与缓存职责,对外面向Android提供HTTP/3,对内也用HTTP/3回源,从而端到端跑通QUIC。

Apache代理缓存与HTTP/3模块选型
Apache从2.4.37之后通过mod_proxy_http3支持HTTP/3反向代理,它依赖mod_http3和底层ngtcp2或quiche库。缓存部分仍由mod_cache及mod_cache_disk负责,二者在过滤器链路上解耦:代理模块拿到响应后交给缓存模块存储,后续命中直接由磁盘或内存返回,无需再次建连。很多团队误以为开了HTTP/3代理就自动有缓存,其实必须显式配置CacheEnable与对应的存储路径。
在编译Apache时要带上--enable-http3和--enable-proxy-http3,否则运行时加载模块会报找不到符号。对于Ubuntu类系统,可优先使用第三方仓库的预编译包,避免自行解决依赖。要注意的是,mod_proxy_http3当前对后端健康检查支持较弱,如果源站仅暴露UDP 443,需要在ProxyPass里写明https://并附加h3参数,例如ProxyPass / api/ https://backend:443/ h3,否则默认回源走HTTP/1.1。
与Nginx的proxy_cache相比,Apache的缓存键默认包含主机头和端口,在多租户网关上更不容易串数据。但在QUIC场景下,由于TLS证书绑定到域名,Apache前端和后端若使用不同证书,回源时会出现证书校验失败。推荐在代理机上部署源站同套证书,或用SSLProxyVerify none临时跳过(仅测试环境)。
面向Android的VirtualHost配置实例
下面给出一个最小可运行的配置片段,前端监听UDP 443提供HTTP/3,后端也用HTTP/3回源,并开启磁盘缓存。Android端只需把域名指向该Apache,即可在支持QUIC的WebView或OkHttp中受益。
<VirtualHost *:443>
ServerName api.ippipp.com
# 前端HTTP/3监听(需mod_http3)
Protocols h3 http/1.1
Listen 443 udp
SSLEngine on
SSLCertificateFile /etc/ssl/api.ippipp.com.crt
SSLCertificateKeyFile /etc/ssl/api.ippipp.com.key
# 缓存配置
CacheRoot /var/cache/apache/h3
CacheEnable disk /
CacheDirLevels 2
CacheDirLength 1
CacheDefaultExpire 3600
# HTTP/3反向代理到后端
<Proxy *>
Require all granted
</Proxy>
ProxyPass / https://192.168.0.1:443/ h3
ProxyPassReverse / https://192.168.0.1:443/
SSLProxyEngine on
</VirtualHost>
上述配置中,Protocols h3 http/1.1保证不支持QUIC的旧Android系统可降级到TCP。缓存根目录权限需让Apache运行用户可写,否则命中率为零且错误日志频繁报权限拒绝。对于带查询参数的动态接口,建议用CacheKeyBaseURL或CacheIgnoreQueryString精细化控制,避免缓存碎片。
在Android侧验证时,可以通过adb shell执行curl -I --http3 https://api.ippipp.com/ping查看响应头是否含Alt-Svc: h3=":443"以及Age头判断命中。若Age为0说明每次都回源,需检查Cache-Control响应头是否允许缓存。源站应返回Cache-Control: public, max-age=60之类指令,Apache才会落盘。
缓存命中率与避坑实践
我们将同一组静态接口在纯TCP代理和HTTP/3代理下做对比:在200毫秒往返延迟的弱网模拟中,TCP代理因每次新连接TLS握手耗时约300毫秒,而QUIC代理首次连接后后续请求零往返复用,缓存命中时边缘直接返回,Android端平均延时从420毫秒降到90毫秒。未命中时QUIC回源也比TCP少一次握手,差距约120毫秒。
常见误区是认为开了mod_proxy_http3就不用管mod_cache的CacheLock。高并发下多个相同请求同时穿透到源站会造成雪崩,应当加CacheLock on让第一个请求回源、其余排队读缓存。另一个坑是Android客户端的QUIC实现若遇到Alt-Svc端口与实际监听不符,会默默退回TCP,因此Apache的Alt-Svc头必须和Listen端口一致。
日志层面建议打开mod_cache的CacheDetail日志,观察cache hit与cache miss比例。如果命中率低,多半是源站设置了Set-Cookie导致默认不缓存,此时可用CacheIgnoreHeaders Set-Cookie强制忽略,但需确认接口本身无状态。经过上述调优,中型资讯类App的接口边缘命中率可达七成以上,源站负载明显下降。