Node.js 超时配置完全指南
无限超时是生产事故的隐形炸弹。本文系统讲解入站请求超时、出站调用超时、Promise 超时包装、超时值的选择方法与超时错误处理。
Node.js 的 http.Server 默认无超时,多数 HTTP 客户端默认无限等待——生产环境必须显式配置:服务端设 requestTimeout/headersTimeout 防慢速攻击,客户端用 AbortSignal.timeout() 控制出站调用,下游超时略小于上游超时逐层收敛。
为什么超时是可靠性基石
下游服务无响应时,没有超时的调用会永远挂起——连接、内存、事件循环资源被持续占用,直至整个服务被拖垮。慢速攻击(Slowloris)正是利用"无限等待":缓慢发送请求头耗光连接池。超时的本质是资源保护。
入站请求:服务端超时
const server = http.createServer(app);
server.requestTimeout = 300_000; // 完整请求 5 分钟上限(Node 18+)
server.headersTimeout = 60_000; // 接收请求头 60 秒(防 Slowloris)
server.keepAliveTimeout = 5_000; // keep-alive 空闲 5 秒
headersTimeout 必须大于 keepAliveTimeout(Node 官方要求),且注意前置 Nginx/负载均衡的超时要与之协调——代理 60 秒、应用 300 秒的组合会让代理先断。
出站调用:客户端超时
// fetch:AbortSignal.timeout 是现代标准写法
const response = await fetch('https://api.example.com/data', {
signal: AbortSignal.timeout(5000), // 5 秒超时
});
// 需要更精细控制(连接 vs 总时长)
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 5000);
try {
await fetch(url, { signal: controller.signal });
} finally {
clearTimeout(timer);
}
axios:{ timeout: 5000 };数据库客户端、Redis 客户端同理——每个网络出口都要有超时。
Promise 通用超时包装
function withTimeout(promise, ms, label = '操作') {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`${label}超时(${ms}ms)`)), ms)
),
]);
}
await withTimeout(heavyComputation(), 3000, '数据处理');
注意:Promise.race 只停止"等待",被包装的操作本身还在跑——资源真正释放要靠 AbortController 传导取消信号。
超时值怎么定
- 从数据出发:看下游 P99 延迟,超时设为 P99 的 2-3 倍;
- 逐层收敛:网关 30s → 服务 25s → 下游调用 20s——上游必须比下游先超时,否则重试雪崩;
- 区分操作类型:读操作严格(秒级),写/批处理宽松(分钟级);
- 留出重试预算:总超时 = 单次超时 × 重试次数 + 退避时间。
超时错误的处理
try {
await fetch(url, { signal: AbortSignal.timeout(5000) });
} catch (err) {
if (err.name === 'TimeoutError' || err.name === 'AbortError') {
// 超时的正确响应:快速失败 + 降级 + 重试(带熔断)
return cachedFallback();
}
throw err;
}
超时错误应该单独统计——超时率上升是下游劣化的最早信号,比错误率更敏感。
确认超时发生在哪个下游
连接建立、等待响应头和读取响应体的超时应记录为不同错误类型。Node.js 应用接入观测云 APM 后,已支持的客户端调用可以提供耗时和错误信息;未被自动覆盖的调用应手动建 Span。把超时预算、重试次数和取消原因写入脱敏日志,再与链路对照,避免把调用方主动取消误判成下游故障。
接入文档:Node.js APM 接入 · 日志采集 · 监控器与告警策略。
常见问题(FAQ)
Q:keep-alive 超时和 LB 空闲超时怎么协调?
应用的 keepAliveTimeout 应大于前置负载均衡的空闲超时——否则应用先关连接,LB 复用已断连接报 502。经典组合:ALB 60s,Node keepAlive 65s。
Q:超时后重试有什么讲究?
指数退避 + 抖动(jitter)防惊群;只重试幂等操作与明确的网络错误;下游已过载时熔断器(circuit breaker)停止重试——重试风暴会把"慢"打成"死"。
Q:setTimeout 计时器本身可靠吗?
事件循环被阻塞时计时器延迟触发——超时精度依赖事件循环健康。事件循环延迟本身也应纳入监控。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。