你大概读到过:HTTPS 为每个 API 请求都新开一条 TCP 连接、重做一次 TLS 握手,因此一条挂着一小时的长连接隧道显得更便宜。这句话的前半句通常是错的。受支持的客户端会把已建立的连接复用于多次交换,而每次调用都新建客户端的基准测试,测的是生命周期而不是防护。本页余下部分说明应该改测什么。

复用到底要求什么

连接保持是 HTTP 模型的一部分,不是什么技巧:一条连接可以承载多次请求与应答的交换。连接会在任一侧关闭、服务器清理空闲或过载连接、或者某条报文头或协议规则说明该连接不可复用时结束。HTTPS 把这段对话放进 TLS 内部,并不要求每次交换都重做握手。因此第二条请求不会重复第一条请求的握手,除非中途有什么结束了这条连接。

复用还有一项应用很容易触犯的条件。打算复用连接的客户端必须把应答体读完,而中途丢弃的应答,正是连接池悄悄消失的常见原因。

请检查库所支持的客户端与连接池的生命周期,而不是假定池化是开还是关。也要记录协商出的 HTTP 版本,因为 HTTP/2 与 HTTP/3 处理连接复用的方式与 HTTP/1.1 不同。请把协商出的版本当作测量的一部分,而不是脚注。

为什么这种比较测的是生命周期

为每个操作新建客户端,再与一条已连接一小时的隧道比较,比较的其实是连接建立与稳态。这是一项合理的基准,但它不是 HTTPS 与 VPN 之争。在同一客户端配置下跑两遍,比只跑一遍更能说明行为,尤其是第一遍会预热解析器或连接缓存时。

请把 API 操作、服务器、账号与数据保持不变,否则比较里就不止一个变量。事先决定计时器放在哪里,因为建立、TLS 协商、应用执行与应答投递对总量的贡献各不相同。只测第一个请求的一个计时器只能刻画建立过程,对你实际运行的稳态流量一无所知。请把计时边界写在数字旁边。

把冷建立与热请求分开

把第一个请求与随后的请求分开测量。然后补上服务真正看到的东西:空闲时段、突发、并发、应答大小与重连。

两条路径都要保持证书校验与 HTTPS 开启,因为关掉它们测的是另一种安全设计,而不是更快的一种。如果隧道改变了路由或网关位置,请把这一差异单独报告,而不是把每一处时间变化都归因于加密,因为路径、丢包与排队可能主导一个小请求。下结论之前,请在同一路由上把每条路径各跑两遍。协商出的协议与计时边界要随两次运行一起记录。

为会关闭的连接做打算

持久并不等于永久。服务器、代理或网络变化都可能在空闲时段关闭连接,客户端必须依靠其库支持的机制恢复。这种恢复有一台状态机:请求可能已在服务器上完成,也可能在服务器读取之前失败,而一个已关闭的套接字不会告诉调用方是哪一种。处在这个位置上,非幂等操作不能简单重放,因为重放会把未知结果变成重复写入。这里热隧道与热连接的表现相同,因为两者都可能在任务在途的瞬间失败。

请使用服务文档中写明的重试或幂等机制,而不是循环重开套接字。请显式测试恢复:关闭一条空闲连接,观察客户端下一步做什么。重开套接字并不能让重复一个改变状态的调用变得安全。

先选边界,再调优

先根据访问与信任要求选定网络边界,再去优化任何东西。VPN 保护的是其对等端之间的那一段,而应用的端点以及隧道之外的任何明文段仍然重要。然后才在这条边界内部调整连接生命周期。“更便宜的隧道”这个结论只来自“每个请求都重做握手”的信念,而这正是本页要检验的信念。

RFC 9112 中关于连接保持的一节说明了复用真正承诺了什么,包括结束连接的各种条件。在相信一个忽略它们的基准之前,请先读一遍。

相关文章