浏览器开发者工具里的协议一栏出现 h3,只能说明这次请求用了 HTTP/3。它没有告诉你页面快了多少,也没有说明服务器不再接受 TCP 连接。如果前面还有 CDN,浏览器与 CDN 使用的协议,甚至可能和 CDN 回源使用的协议不同。
讨论 QUIC 时,先把观察范围缩到两端:哪一个客户端,连到哪一台服务器,中间经过什么网络。离开这些条件,“HTTP/3 更快”很难成为一个有用的工程结论。
一个丢包为什么会拖住别的请求
假设浏览器通过同一条 HTTP/2 连接请求三个资源:样式文件、封面图片和推荐列表。HTTP/2 可以交错发送不同请求的数据,不用等一个请求结束再处理下一个。但这些内容在 TCP 看来,仍是一串按顺序交付的字节。
如果缺失了一段 TCP 数据,即使后续字节已经到达,接收端也不能跳过缺口,把后面的完整字节流交给 HTTP/2。于是,丢失的数据可能属于图片,推荐列表却也在等它。应用层能区分三个请求,底层的单条 TCP 字节流不能按 HTTP 请求替它们分开等待。
QUIC 在传输层就提供多条流。某条流缺了一段数据,另一条流已经收到的内容不必因为同一个字节流缺口而停止交付。HTTP/3 利用这些流承载请求与响应。这是 RFC 9114介绍 HTTP/3 时强调的一项变化。
这里有两个限制容易被省略。
首先,几个请求仍共享连接上的资源。发生丢包后,发送端的拥塞控制可能减少发送速率,其他流也会受到影响。QUIC 没有为每个网页资源变出一条独享链路。其次,收到资源并不等于浏览器马上可以渲染:脚本执行、样式计算和应用依赖仍可能让页面等待。把网络层减少的一种等待,写成“彻底消除阻塞”,会让排障走偏。
HTTP/3 的字段压缩也有自己的依赖。QPACK 允许编码器使用动态表;如果接收端尚未得到某个字段块依赖的表更新,就需要等待。协议用限制阻塞流数量等机制约束这种情况。部署者不需要因此放弃 HTTP/3,但应该知道它仍存在应用协议层的等待。RFC 9204给出了这一设计。
UDP 在这里负责把包送到门口
“UDP 不保证可靠,所以 QUIC 不可靠”是把两层功能混在了一起。UDP 为 QUIC 承载数据报;确认、数据恢复、流量控制等工作由 QUIC 实现。一个使用 QUIC 的 HTTP 请求,不要求业务代码自己处理每个丢包。RFC 9000定义了这套传输机制。
选择 UDP,也考虑了现有网络的部署条件。许多路由器、防火墙、NAT 和负载均衡器已经知道怎样处理 UDP。QUIC 的实现者可以利用这个入口,在端点升级协议代码,而不必等待所有中间设备理解一个全新的传输协议号。
这只是部署条件更有利,并不保证畅通。有些企业网络直接限制 UDP,有些路径上的 UDP 表现与 TCP 不同。因此,一个提供公共网站服务的团队,不能在打开 UDP 443 后就删除 TCP 443。可达性需要按真实用户网络统计。
可以用一个具体的运维问题检验这点:办公室 Wi-Fi 打开网站正常,接上公司 VPN 后首次访问变慢,随后又正常。此时应该同时记录 QUIC 尝试、连接失败和回退时延,而不是只观察最终成功的 HTTP 请求。最终显示 h2,不代表此前没有在失败的 QUIC 尝试上花过时间。
网络设备看不见的部分,更容易改
当中间设备长期观察某个明文字段,开发者可能开始依赖它的一种表现。几年后,协议实现者想改变这个字段的用法,却发现设备把新流量当作异常丢弃。即使端点支持升级,传输路径也会限制升级。
QUIC 尽量减少这种依赖。它保护大量传输细节,并把跨版本保持不变的公开属性单独写进规范。RFC 8999 的意义就在于区分哪些信息可以作为稳定约定,哪些内容应允许未来版本改变。实现网络设备时,不能因为某个 QUIC 版本今天如此编码,就把它当成永久规则。QUIC 的版本无关属性
加密也会改变排障方式。只靠中间链路抓包,未必能像查看 TCP 序列号那样解释应用为什么在等。团队应从端点取得连接统计,把协议版本、握手结果和请求耗时关联起来。这里需要的是服务端库与运维工具的支持,不能指望更换协议后,原有仪表盘自动给出同样的信息。
0-RTT 是有条件的提前发送
QUIC 将传输和 TLS 握手结合起来。对满足恢复条件的连接,客户端有机会在开始连接时发送早期应用数据。于是人们习惯用“0-RTT”形容它的吸引力。
但第一次连接一个从未访问过的服务器,和带着可用会话状态回来,是不同情况。服务端也可能拒绝早期数据。更重要的是,早期数据具有重放风险,应用必须考虑同一个操作被重复接收的后果。RFC 9001 的 0-RTT 章节讨论了这一点。
设想一个“领取一次优惠券”的接口。如果实现者只看 HTTP 方法,发现它用了 GET,就允许早期执行,实际上领取操作仍可能改变数据库。读写语义应由业务操作判断,不能只看 URL 或方法名。支付、创建订单这类动作,需要和服务端的早期数据策略一起检查。
测性能时也要分开统计新连接和恢复连接。把已经访问过的客户端,与另一组全新客户端混在一起比较,可能把缓存和会话恢复的差异误算成协议收益。
换了网络,连接也未必需要重建
手机从 Wi-Fi 切到移动网络时,客户端地址可能发生变化。QUIC 通过连接 ID 帮助端点识别连接,并提供路径验证与迁移机制。因此,连接不必始终绑定在最初的 IP 和端口组合上。
不过,连接 ID 不是绕过验证的通行证。新路径仍需要验证,端点可能限制主动迁移,新的网络也可能限制 UDP。应用层是否保持原来的操作,还取决于连接有没有超时、服务端实现和业务自身的状态。RFC 9000 的连接迁移章节规定了相关行为。
一个聊天产品和一个后台报表系统,对这项能力的评价可能不同。前者的用户边走边用,网络变化频繁;后者的管理员坐在固定网络里,主要等待数据库查完报表。应先看用户实际遇到的等待发生在哪里,再决定测试重点。
“更快”应该用什么数据证明
部署前先确认现有请求耗时中有多少来自连接和网络。如果主要耗时在数据库查询,把 HTTP/2 换成 HTTP/3 不会减少那条 SQL 的执行时间。
然后按网络类型、地区和客户端分组,比较握手失败率、请求完成时间与尾部时延。对站点运营者而言,还需要观察服务器 CPU 和连接资源消耗。单次测速只展示一次请求;它没有覆盖高并发、路径变化和繁忙时段。
丢包测试也不能只设置一个丢包百分比就结束。相同平均丢包率下,零散丢包与突发丢包对体验可能不同。发送端如何检测丢失、何时探测超时、怎样限制在途数据,都会参与最终结果。QUIC 的丢失检测与拥塞控制有独立规范 RFC 9002,不要把这些机制理解成 UDP 自动提供的性能优势。
如果本机 curl 编译时包含 HTTP/3 支持,可以先确认两种连接方式:
curl --version
curl --http2 -I https://example.com/
curl --http3-only -I https://example.com/
将 example.com 换成要检查的域名。这些命令检查连通性,尚不足以构成测速。--http3-only 用于检查 HTTP/3 本身是否成功;需要观察回退行为时,应另行使用支持回退的选项和相应客户端。curl 的HTTP/3 文档介绍了构建支持与选项差异。系统自带的 curl 没有编译相关功能时,命令失败首先说明本地工具不支持,不能直接归因于服务器。
最后再看服务路径。如果浏览器连接 CDN,测试需要区分浏览器到边缘节点和边缘节点到源站。用户下载慢,可能是边缘缓存未命中,也可能是源站生成响应慢。在开发者工具里确认 h3,只完成了这条链路中的一项检查。
给已有网站启用 HTTP/3
可以从可回退的部署开始:保留 TCP 服务,在受控范围内开放 QUIC,确认防火墙、负载均衡和服务端配置共同支持它。先看失败率,再看成功请求的时延。只比较成功样本,会把那些等到超时、最后换协议的用户排除在外。
如果用户主要使用移动网络,或者站点有大量并发资源请求,可以优先检查网络切换与丢包时的体验。若收益有限,就保留测量记录,不必为了协议栏出现 h3 强行宣称升级成功。协议选择最终要落到可重复观察的结果:用户完成了什么操作,等了多久,哪些网络仍然失败,以及团队能否解释这些失败。











