Node.js机器人CPU占用100% 寻求不减速的优化方案
问题
近期用Node.js开发了一个机器人,CPU占用率达到100%,运行负担极大。使用了node-fetch、async.parallel和setInterval,不确定CPU满载的原因,猜测可能与setInterval队列有关。代码如下:
async.parallel({ task1: function () { setInterval(function () { let itemRandomizer = config.generalInfo.itemList[ Math.floor(Math.random() * config.generalInfo.itemList.length) ]; let cookieRandomizer = randomCookies[Math.floor(Math.random() * randomCookies.length)]; fetch(url, { method: "GET", agent, headers: { "Content-Type": "application/json", cookie: `cookie=${cookieRandomizer}`, }, }) .then((res) => res.json()) .then((json) => { checks++; //console.log(checks, itemRandomizer, json.data[0].price) }) .catch((err) => {}); }, 0); }, task2: function () { /* same code as task 1 (total 20 tasks) */ }, });
不想增大执行间隔,希望保持现有速度,但100%的CPU占用实在不合理,请问有什么解决办法?setInterval是否本身就高耗CPU?另外我认为线程比async.parallel更耗CPU,所以没有使用线程。
解决办法
CPU满载的核心原因:你开了20个
setInterval(..., 0)定时器,Node.js中setInterval的最小执行间隔受事件循环限制,不可能真的0毫秒触发,但20个定时器会疯狂把回调塞进事件循环队列。虽然每个回调里的fetch是异步的,但回调本身执行速度极快,定时器会立刻再次调度下一次执行,导致主线程被这些定时器的调度逻辑占满,直接拉满CPU。另外async.parallel在这里完全没用——它的作用是等待一组异步任务完成,但你每个task只是调用setInterval(这个函数是同步返回的),所以async.parallel瞬间就执行完,20个定时器直接同时启动,没有任何并发控制。setInterval本身不耗CPU:只有当你用它无限制触发高频回调,或者回调里有同步阻塞代码时,才会导致CPU飙升。你的问题是高频+多实例的定时器调度,而非
setInterval本身。具体优化方案:
- 删掉async.parallel和所有setInterval,改用并发控制维持请求速度。比如用
p-limit库(或自己实现简单并发队列),固定同时运行的请求数量,每次请求完成后立刻发起新请求,既保持高请求频率,又不会让定时器占用CPU。
示例思路:const pLimit = require('p-limit'); const limit = pLimit(10); // 控制同时10个请求在跑,可根据实际调整 // 无限循环发起请求,由p-limit控制并发 async function runTasks() { while(true) { await limit(async () => { let itemRandomizer = config.generalInfo.itemList[Math.floor(Math.random() * config.generalInfo.itemList.length)]; let cookieRandomizer = randomCookies[Math.floor(Math.random() * randomCookies.length)]; try { const res = await fetch(url, { method: "GET", agent, headers: { "Content-Type": "application/json", cookie: `cookie=${cookieRandomizer}`, }, }); const json = await res.json(); checks++; } catch (err) { // 补全错误处理,避免隐藏问题 console.error('请求失败:', err); } }); } } runTasks(); - 抛弃setInterval(0)写法:这种写法会让事件循环被定时器回调淹没,完全没必要。通过并发控制维持请求密度,比定时器高效得多。
- 补全错误处理:之前的空
catch会隐藏请求失败的问题,比如某些请求反复失败可能导致无效CPU消耗,加上错误处理能排查潜在问题。
- 删掉async.parallel和所有setInterval,改用并发控制维持请求速度。比如用
关于线程的误解:Node.js单线程模型在IO密集型任务(比如你的网络请求)上效率很高,不需要手动开线程。你之前的问题根本不是线程问题,而是定时器滥用导致的调度过载。
内容的提问来源于stack exchange,提问作者cooldevnocap

