Node.js多线程爬取Axios POST报EADDRINUSE 443错误原因及修复
错误产生原因
- 该错误本质是短时间内发起的出站HTTPS请求过多,耗尽了本机可用临时端口,并非目标服务端口被占用。
- 多线程爬虫运行时,每个线程爬取完成后会产生至少4万条待提交数据,如果未做批量聚合、并发控制,会在极短时间内向目标IP
18.62.228.13:443发起海量TCP连接。TCP连接关闭后会进入TIME_WAIT状态,默认需要等待1-2分钟才会彻底释放端口,当新请求到来时没有可用临时端口分配,就会抛出EADDRINUSE异常。 - 默认配置下axios不会开启TCP连接复用(keep-alive),每次POST请求都会新建独立TCP连接,请求完成后直接断开,进一步放大了端口消耗速度。加上worker线程之间资源隔离,各线程独立发起请求,全局并发数完全失控,端口耗尽是必然结果。
可行修复方案
1. 开启TCP连接复用,复用axios实例
不要每次请求新建axios实例,全局配置开启keep-alive,让多个请求复用同一个TCP连接,从根源减少新建连接数:
const axios = require('axios'); const http = require('http'); const https = require('https'); // 每个worker线程内只初始化一次该实例,重复复用 const httpAgent = new http.Agent({ keepAlive: true, maxSockets: 50, // 单实例最大并发socket数 maxFreeSockets: 10 // 最大空闲socket数 }); const httpsAgent = new https.Agent({ keepAlive: true, maxSockets: 50, maxFreeSockets: 10 }); const requestClient = axios.create({ httpAgent, httpsAgent, timeout: 30000 });
2. 做批量聚合+并发限流,从量级上减少请求数
- 不要单条数据发一次POST请求,将4万条数据拆分为每批500-1000条的大块,每次请求提交一整批,直接把请求总数从4万级降到几十级。
- 严格控制单worker线程内的提交并发,建议单worker同时发起的提交请求不超过2个,全局总并发(worker数量*单worker并发数)不超过10,禁止直接用
Promise.all一次性发起所有请求。
参考实现代码:
// 数据按指定大小分块 function splitChunks(dataList, chunkSize = 1000) { const chunks = []; for (let i = 0; i < dataList.length; i += chunkSize) { chunks.push(dataList.slice(i, i + chunkSize)); } return chunks; } async function batchSubmit(allData, submitUrl) { const chunks = splitChunks(allData, 1000); const concurrency = 2; // 单worker并发数 const runningTask = []; for (const chunk of chunks) { const task = requestClient.post(submitUrl, { data: chunk }) .then(res => { /* 处理成功逻辑 */ }) .catch(err => { /* 处理失败重试逻辑 */ }) .finally(() => { runningTask.splice(runningTask.indexOf(task), 1); }); runningTask.push(task); if (runningTask.length >= concurrency) { await Promise.race(runningTask); } } await Promise.all(runningTask); }
3. 操作系统参数调优(辅助方案)
如果程序运行在Linux环境,可以调整内核参数扩大临时端口范围、加快TIME_WAIT连接回收,作为代码优化的补充:
- 编辑
/etc/sysctl.conf添加以下配置:
# 扩大临时端口范围 net.ipv4.ip_local_port_range = 1024 65535 # 允许复用TIME_WAIT状态的连接 net.ipv4.tcp_tw_reuse = 1
- 执行
sysctl -p让配置生效。
注意:系统参数调优不能替代代码层面的优化,如果不做连接复用和并发控制,即使调大端口范围,依然会出现端口耗尽的问题。
内容的提问来源于stack exchange,提问作者nikita cohen
相关产品推荐
相关产品推荐

