Node.js HTTP Agent 和 keep-alive 有什么作用?
简化版
HTTP Agent 负责管理 Node.js 客户端请求的连接复用。开启 keep-alive 后,可以复用 TCP 连接,减少握手开销,提高吞吐;但要配置最大连接数、空闲超时和错误处理,避免连接泄漏或压垮下游。
详细版
Node.js 发起 HTTP 请求时,Agent 会管理 socket。 默认情况下连接复用策略可能不满足高并发服务需要。
import http from 'node:http'
const agent = new http.Agent({
keepAlive: true,
maxSockets: 100,
maxFreeSockets: 20,
timeout: 30_000
})
在 BFF、网关、微服务调用里,合理的 keep-alive 能显著减少 TCP/TLS 握手开销。面试要强调:连接池不是越大越好,要按下游容量和自身并发配置。
完整版教学
一、每次新建连接的成本并不低
HTTP 调用不是只有业务接口耗时。 如果每次请求都新建 TCP 连接,还要经历握手;HTTPS 还要 TLS 握手。 短请求高频调用时,连接建立成本会很明显。
无复用:
DNS -> TCP 握手 -> TLS 握手 -> 请求 -> 响应 -> 关闭
keep-alive:
首次建连 -> 多次请求复用同一连接
假设一次 TCP/TLS 建连需要 50ms,接口业务处理只要 20ms。 不复用连接时,大部分时间都花在建连上。 keep-alive 能把重复握手成本摊薄。
二、Agent 是客户端连接池管理者
Node.js 的 HTTP Agent 负责为请求分配 socket。 它会维护正在使用的连接、空闲连接和等待队列。 配置不当时,要么连接太少导致排队,要么连接太多压垮下游。
const agent = new http.Agent({
keepAlive: true,
maxSockets: 200,
maxFreeSockets: 50
})
| 配置 | 含义 | 风险 |
|---|---|---|
keepAlive | 是否复用连接 | 不开会增加握手 |
maxSockets | 每个目标最大并发连接 | 太大会压下游 |
maxFreeSockets | 最大空闲连接数 | 太大会占资源 |
timeout | socket 超时 | 太长会挂住 |
一个服务如果同时调用 5 个下游,每个下游 maxSockets 设 200,理论上可能建立 1000 条连接。 这要和机器资源、下游限流和负载均衡能力一起评估。
三、keep-alive 提升吞吐但会带来空闲连接管理
连接复用不是没有代价。
空闲 socket 会占用文件描述符和内存。
如果对端关闭了空闲连接,本端复用时可能遇到 ECONNRESET。
因此要配置空闲超时和错误重试。
连接池状态:
busy sockets -> 正在请求
free sockets -> 等待复用
closed sockets -> 被回收
如果系统 QPS 从 1000 降到 10,空闲连接还长时间保留,就会浪费资源。 合理的 free socket 上限和 timeout 能让连接池自动收缩。
四、BFF 和网关最容易从 Agent 配置获益
前端 BFF 常把一个页面请求拆成多个后端接口调用。 如果每次都新建连接,BFF 自身延迟会被放大。 连接复用能降低 P95 延迟。
页面请求
-> BFF
-> 用户服务
-> 商品服务
-> 推荐服务
假设 BFF 每个页面调用 3 个服务,每个服务建连多 40ms。 没有连接复用时,串行调用可能多出 120ms。 并行调用也会增加下游连接压力。
五、连接池大小要根据下游容量配置
很多人把 maxSockets 调很大,以为吞吐就会提升。
如果下游只能稳定处理 100 并发,你给它打 1000 条连接,只会增加排队、超时和熔断。
连接池是背压的一部分。
合理配置:
自身并发
下游容量
超时时间
重试策略
连接池大小
例如下游 P95 100ms,希望每秒 500 请求,理论并发约 50。 再加一些冗余,连接池设到 100 可能够用,不必一上来 1000。
六、现代 fetch/undici 也有类似连接管理
Node.js 现代版本里的 fetch 基于 undici,也有 dispatcher、连接池等概念。
虽然 API 名称不同,但思想一样:复用连接、限制并发、管理空闲资源。
面试中可以把 Agent 作为经典入口,再说明新客户端库也要关注连接池。
记忆钩子:Agent 像打车调度站,keep-alive 让同一辆车多跑几单。
回答时别只说“提高性能”。 要说清楚减少握手、管理 socket、控制下游压力和处理空闲连接。
七、常见误区与追问
- 误区:keep-alive 开了就一定没有问题。 还要配置连接数、空闲连接、超时和错误重试。
- 误区:maxSockets 越大吞吐越高。 过大可能压垮下游或让本机文件描述符耗尽。
- 误区:连接复用只影响服务端。 Node 作为 HTTP 客户端调用下游时同样需要连接池。
- 追问:为什么 HTTPS 更需要复用? TLS 握手成本更高,复用连接能减少重复握手。
- 追问:ECONNRESET 和 keep-alive 有什么关系? 对端可能关闭空闲连接,本端复用时会遇到连接重置,需要重试和超时处理。
- 追问:BFF 中如何配置? 按下游服务分别配置连接池、超时和限流,不要所有服务共用一个粗暴默认值。
八、加强记忆
HTTP Agent 题抓“连接复用 + 连接池治理”。keep-alive 减少 TCP/TLS 握手,Agent 管 socket;配置要看下游容量,配合超时、重试和空闲连接回收。性能优化的另一面是别把下游打爆。