4、连接复用:HTTP Keep-Alive、连接池、多路复用对比

说到连接性能优化,有个问题我一直觉得特别有意思:为什么明明 TCP 三次握手已经够快了,我们还要想尽办法复用连接?

我早年做网关优化时,亲眼见过一个场景——客户端每秒发起上千次请求,每次都是新建 TCP 连接。三次握手 + 慢启动,光握手开销就占了请求总耗时的 40%。那时候我就意识到:减少握手次数,比优化握手本身更划算。

这一章,我们来聊聊三种主流的连接复用手段:HTTP Keep-Alive、连接池、多路复用。它们的目标一致——让 TCP 连接活得更久、干得更多。

核心观点:连接复用的本质,是用「已建立的连接」去换「新建连接的时间」。三次握手的 1.5 RTT 开销,在复用场景下只需要支付一次。

连接复用技术演进对比 连接复用技术演进对比 短连接(无复用) 每次请求新建 TCP 三次握手 × N 次请求 HTTP Keep-Alive 连接复用,串行请求 三次握手 1 次 + 串行传输 连接池 多连接复用池 并发 + 复用 HTTP/2 多路复用 单连接内并行多个请求/响应流 三次握手 1 次 + 并行传输 💡 演进逻辑: 从「用完即弃」到「串行复用」再到「并发复用」,核心是降低连接建立开销,提升链路利用率。

4.1 HTTP Keep-Alive:最朴素的复用

HTTP Keep-Alive 是连接复用的「第一课」。说白了,就是让 TCP 连接在完成一次 HTTP 请求/响应后不关闭,等着下一个请求继续用。

在 HTTP/1.0 时代,每个请求都要新建连接。后来 HTTP/1.1 把 Keep-Alive 变成了默认行为。你想想看,如果一个页面有 80 个资源,短连接需要 80 次三次握手,Keep-Alive 只需要 1 次。这差距,肉眼可见。

我的经验:Keep-Alive 的 timeout 设置很关键。太短(比如 5 秒)起不到复用效果;太长(比如 300 秒)会占用服务器 fd 资源。我一般建议 30-60 秒,配合 max_requests 限制单连接处理请求数,防止内存泄漏。

但 Keep-Alive 有个硬伤:串行。同一个连接上,请求必须排队。前一个请求没响应,后一个就得等着。这就是 HTTP 队头阻塞(HOL blocking)的根源。

4.2 连接池:并发复用

既然单连接串行太慢,那就多搞几条连接。连接池的思路很简单:维护一组已建立的 TCP 连接,需要时从池子里取,用完放回。

我在项目中遇到过这样一个场景:微服务间 RPC 调用,每次调用都新建连接,导致 CPU 在 connect 系统调用上消耗了 15%。后来引入连接池,预创建 10-20 条连接,调用延迟直接降了 40%。

连接池的核心参数有这几个:

参数 含义 建议值
最大连接数 池中最多维护的连接数 根据并发量估算,一般 10-50
最小空闲数 保持至少多少条空闲连接 2-5,避免突发流量时建连
连接超时 空闲连接的最大存活时间 30-120 秒
获取超时 从池中获取连接的最大等待时间 100-500 ms

⚠️ 避坑指南:我曾经遇到过一个线上事故——连接池最大连接数设了 200,但服务端最大文件描述符只有 1024。高峰期连接池占满 fd,新连接进不来,旧连接又释放不掉,最终导致服务雪崩。记住:连接池大小要和服务端容量对齐。

连接池的典型实现有:Apache HttpClient 的 PoolingHttpClientConnectionManager、Redis 的 JedisPool、数据库连接池 HikariCP。它们底层做的事情都一样——用空间换时间。

4.3 多路复用:单连接并行

连接池虽然能并发,但每条连接都有自己的开销。有没有办法在一条连接上并行处理多个请求?

有。这就是 HTTP/2 的多路复用。

HTTP/2 引入流(Stream)的概念。一条 TCP 连接上可以同时存在多个流,每个流独立传输请求和响应。数据被拆分成更小的帧(Frame),帧与帧之间交错传输,接收方按流 ID 重组。

我刚开始看 HTTP/2 协议时,觉得这玩意儿太复杂了。但后来在 CDN 项目里实测,多路复用比 HTTP/1.1 的 Keep-Alive 快了将近 3 倍。为什么?因为彻底解决了队头阻塞。

关键区别:HTTP/1.1 的 Keep-Alive 是「串行复用」,连接池是「多连接并发复用」,HTTP/2 多路复用是「单连接并行复用」。三者的复用粒度依次提升。

来看一个简单的对比:

特性 HTTP Keep-Alive 连接池 HTTP/2 多路复用
连接数量 1 条 多条(池化管理) 1 条
请求方式 串行 多连接并行 单连接并行
队头阻塞 有(HTTP 层) 无(连接间独立) 无(流间独立)
资源开销 低 中(多条连接) 低(单连接)
适用场景 低频请求、小资源 高并发、微服务 RPC 大量小资源、实时通信

4.4 如何选择?

没有银弹。三种方案各有各的适用场景。

  • HTTP Keep-Alive:适合请求频率不高、资源量不大的场景。比如 API 调用、简单的 Web 页面。实现简单,浏览器默认支持。
  • 连接池:适合高并发、长连接的场景。比如数据库访问、Redis 操作、微服务间 gRPC 调用。需要客户端库支持,配置得当能大幅提升吞吐。
  • 多路复用:适合大量小资源并行加载的场景。比如现代 Web 页面(几十个 CSS/JS/图片)、实时推送服务。需要 HTTP/2 协议支持,服务端和客户端都要升级。

我的建议:如果你在维护一个老系统,先上 Keep-Alive,零成本改造。如果并发上来了,加连接池。如果追求极致性能,升级到 HTTP/2 多路复用。别一上来就搞最复杂的方案——性能优化的第一原则是:用最小的改动解决最大的问题。

嗯,这一章就到这里。连接复用是 TCP 性能优化的「第一桶金」,搞懂了它,你就已经超越了 80% 的开发者。