在 Node.js 或前端项目中,axios 并发请求遇到 ECONNRESET 通常意味着连接被服务端或网络中间件强制切断。最稳妥的优化方案是引入 axios-retry 插件配合指数退避策略,同时必须使用队列限制最大并发数,避免流量风暴。
先说结论:单纯增加重试次数可能加剧服务器压力,建议结合重试机制与并发控制一起处理。
- 先确认:区分是网络波动导致的 ECONNRESET 还是服务端主动限流。
- 先处理:接入 axios-retry 配置指数退避,并添加重试日志监控。
- 再控制:使用 p-limit 限制并发请求数量,防止连接数耗尽。
- 后验证:观察重试日志,确保没有陷入无限重试循环或非幂等请求重复提交。
安装与基础配置
不需要手动编写复杂的重试逻辑,社区成熟的 axios-retry 插件已经覆盖了大部分场景。首先安装依赖:
npm install axios-retry `--save`
如果需要进行并发控制,建议同时安装 p-limit:
npm install p-limit `--save`
然后在初始化 axios 的地方进行拦截配置,默认开启 3 次重试并使用指数退避,同时添加重试日志回调:
import axios from 'axios';
import axiosRetry from 'axios-retry';
axiosRetry(axios, {
retries: 3,
retryDelay: axiosRetry.exponentialDelay,
onRetry: (retryCount, error, requestConfig) => {
console.warn(`[Axios Retry] Attempt ${retryCount} for ${requestConfig.url} | Error: ${error.code}`);
}
});核心优化:并发控制
重试机制不能解决并发过高的问题。在业务代码层,必须限制同时进行的请求数量。如果是 Node.js 环境,结合 p-limit 控制并发度,避免瞬间流量打满带宽或连接数。
完整代码示例:
import pLimit from 'p-limit';
import axios from 'axios';
// 限制同时最多 5 个请求
const limit = pLimit(5);
const urls = ['url1', 'url2', 'url3', 'url4', 'url5', 'url6'];
// 将请求任务放入队列
const tasks = urls.map(url =>
limit(() => axios.get(url))
);
// 执行所有任务
Promise.all(tasks)
.then(results => {
console.log('All requests completed');
})
.catch(err => {
console.error('Some requests failed', err);
});安全配置:避免非幂等请求重试
对于 POST、PUT 等修改数据的接口,重试可能导致数据重复提交。务必在配置中排除非幂等方法,或确保后端支持幂等性。建议在 retryCondition 中增加方法判断:
axiosRetry(axios, {
retryCondition: (error) => {
// 获取请求方法
const method = error.config?.method?.toLowerCase();
// 非幂等请求直接放弃重试
if (['post', 'put', 'patch'].includes(method)) {
return false;
}
// 仅对网络错误和 5xx 服务端错误重试
return error.isAxiosError && (
error.code === 'ECONNRESET' ||
error.code === 'ECONNABORTED' ||
(error.response && error.response.status >= 500)
);
}
});怎么验证是否生效
1. 检查控制台日志:触发网络波动时,观察是否打印了 [Axios Retry] Attempt 日志,确认错误触发时是否进入了重试逻辑。
2. 观察错误率:对比优化前后的 ECONNRESET 报错频率,成功率的提升应该是渐进的,而非瞬间归零。
3. 验证并发限制:在高并发测试下,监控服务端连接数,确认客户端同时发出的请求数未超过设定的 limit 值(如 5)。
常见坑与排查
1. 无限重试循环:如果没有设置最大重试次数(retries),网络持续波动时程序会卡死。必须设定上限,并在失败后抛出异常。
2. 忽略超时设置:重试会增加总耗时。如果业务对实时性要求高,需配合 timeout 参数使用,避免单个请求链路过长阻塞线程。
axios.get('/api/data', {
timeout: 5000, // 单个请求超时 5 秒
// axios-retry 会自动处理重试间的延迟
});3. TLS 握手问题:在 HTTPS 请求中,如果 TLS 握手阶段发生超时或证书验证失败,也可能抛出此类错误。错误栈中常包含 TLSWrap.onStreamRead 信息,此时重试通常无效,需检查证书或网络代理配置。