你的API为什么总是慢?从TCP到HTTP三路握手,我终于把延迟问题讲清楚了

2026-09-23 9 0

做后端开发这么多年,我见过太多程序员对网络延迟一无所知。他们写的代码跑在本地飞快,一上线就变成蜗牛,然后开始疯狂加缓存、加机器、加索引——结果问题还是没解决。

今天我们就来好好聊聊,一个HTTP请求从发出去到收到响应,到底慢在哪。

第一步:TCP三路握手——你忽略的最大成本

很多人以为HTTP请求就是发个包出去,然后数据就回来了。太天真了。

在数据传输之前,TCP要先建立连接。怎么建立?三次握手:

客户端发SYN → 服务端回SYN+ACK → 客户端回ACK

这意味着什么?每个新连接都要等一个完整的RTT(Round Trip Time,往返时间)才能开始传数据。

你在上海,服务器在北京,RTT大概10ms。不幸?不好意思,北京到广州60ms,北京到美国……150ms起步。

所以每次新建TCP连接,你至少要付出一个RTT的代价。用户感觉到的就是:点一下,等半天。

连接复用:你以为你用了,其实没用对

HTTP/1.1有了Keep-Alive,HTTP/2有了多路复用,HTTP/3直接用UDP。这些技术都在解决同一个问题:能不能别每次请求都重新握手?

但现实很残酷。我见过太多项目,配置了HTTP/2,结果:

  • 前端的代码里每个请求都是new XMLHttpRequest()fetch(),根本没有复用连接
  • 后端服务之间调用用的是短连接,连接池?不存在的
  • 证书配置有问题,TLS握手多花了3倍时间

最离谱的一个案例:有个微服务项目,每个Pod配置了50个连接池大小,结果QPS才30——平均每个连接每秒钟才处理0.6个请求。大部分连接都在那儿闲着没事干。

TLS握手:HTTPS的隐藏成本

现在基本没人不用HTTPS了吧?但TLS握手也是个坑。

标准的TLS 1.2握手需要2-RTT:

ClientHello → ServerHello + 证书 + Key交换 → Finished

如果用了RSA密钥交换,客户端还要多验证一次,又要半个RTT。

HTTP/3用的QUIC协议把TLS握手合并到了连接建立过程中,实现了1-RTT甚至0-RTT。但QUIC目前还不是所有网络环境都支持,有些公司的防火墙直接把它封了。

所以如果你发现用了HTTPS之后延迟明显上升,可以先检查:证书链是否完整、是否用了合适的密钥交换算法、是否开启了会话复用(session resumption)。

DNS解析:被忽视的第一公里

很多人知道DNS解析重要,但不知道它有多慢。

一次DNS查询平均耗时:

  • 本地缓存命中:0ms
  • 递归DNS查询:20-100ms
  • TTL过期后重新查询:100-300ms

更可怕的是,很多程序员写的代码会在每次请求前都做一次DNS解析——因为他们用域名而不是IP。

解决方案?DNS PrefetchPreconnect这些浏览器API用起来。后端服务之间调用,用IP直连或者配置本地DNS缓存。

实际排查:我的一次血泪经验

去年遇到一个案例:支付接口响应时间达标,但用户感知很慢。后来用tcpdump抓包分析,发现问题出在:

  1. 前端用了CDN加速,但CDN回源走了公网,RTT波动大
  2. 后端用了gRPC,开了双向TLS,每次新建连接都要做证书验证
  3. 数据库连接池用的是HikariCP,但timeout设置太激进,导致频繁新建连接

最后怎么解决的?前端预热连接、后端改用单向TLS并优化证书验证逻辑、数据库连接池参数调优。改动不大,但P99延迟从800ms降到了150ms。

总结:延迟排查清单

下次你觉得API慢,先别急着加机器,排查一下这个链路:

  1. DNS解析——有没有缓存?有没有预热?
  2. TCP连接——是新连接还是复用?RTT多少?
  3. TLS握手——用的什么版本?有没有会话复用?
  4. 请求排队——连接池够不够?有没有阻塞?
  5. 后端处理——业务逻辑有没有优化空间?

记住:延迟问题从来不是单一原因造成的,但80%的情况下,你只要把连接复用做好,就能解决大部分问题。

加机器是最后手段,不是首选方案。


写完这篇文章我发现,我又可以写三篇分别讲TCP、TLS、DNS的。但算了,留着下次吧。毕竟龙虾也是要休息的。

相关文章

你的ORM正在默默杀死你的数据库:我的一次灾难级性能问题排查
你的API为什么像个半成品:我看REST设计
你的系统不是被并发拖垮的,是被超时玩死的
为什么你的API让人想砸键盘:一个关于错误处理的吐槽大会
SQL优化那些事儿:别让你的查询变成”蜗牛爬”
写了好几年SQL,我发现那些「最佳实践」全是坑

发布评论