puppeteer-cluster多并发执行:设置maxConcurrency为5仍仅单任务运行
问题根源
你当前代码始终只能跑1个任务的核心原因,是for循环里每轮都await cluster.execute(url)。这个方法会阻塞当前循环流程,等当前url的任务从启动浏览器、打开页面到完全执行结束,才会进入下一次循环判断下一个url,完全抵消了puppeteer-cluster的并发调度能力,自然跑不满配置的5个并发。
方案一:无强返回值需求,兼顾满并发和队列内存控制
如果你不需要每个任务的返回结果,只是批量处理url,之前用cluster.queue()遇到的内存过高问题,本质是没有对入队速度和队列长度做限制。puppeteer-cluster本身自带队列观测和长度限制能力,不需要自己维护队列计数器:
- 启动集群时直接配置
maxQueueSize参数,给待处理队列设硬上限,队列满了就自动触发背压,不会无限往内存里堆数据 - 直接读取
cluster.queue.size属性,就能实时拿到当前队列的待处理任务数,完全可以在入队环节做流量控制
示例代码:
const cluster = await Cluster.launch({ monitor: false, concurrency: Cluster.CONCURRENCY_BROWSER, maxConcurrency: 5, // 设队列上限为最大并发的4倍,按自身内存调整即可 maxQueueSize: 20 }) // 绑定实际的任务处理逻辑 await cluster.task(async ({ page, data: url }) => { await page.goto(url) // 补充你的页面数据提取、操作逻辑 }) // 按可控速度把url入队 for (let url of urls) { // 读当前队列长度,满了就暂停入队,等任务被消耗 while (cluster.queue.size >= cluster.maxQueueSize) { await new Promise(resolve => setTimeout(resolve, 200)) } cluster.queue(url) } // 等所有队列任务执行完再关闭集群 await cluster.idle() await cluster.close()
这个写法的执行效果是:所有url按可控节奏入队,集群自动调度5个空闲浏览器实例拉取任务,很快就能跑满5个并发;同时队列长度不会超过设定值,不会出现无限制堆任务占满内存的问题。
方案二:需要拿到任务执行返回值的场景
如果你确实需要等每个任务跑完拿到结果,再决定后续逻辑,就不要在for循环里逐次await cluster.execute()。可以自己加一层并发控制,保证执行中的任务数稳定维持在5个,既拿到任务返回值,又不把并发压成1:
示例代码:
// 自定义并发控制逻辑,保证同时执行的任务数不超过上限 async function runWithConcurrency(taskList, concurrencyLimit) { const executing = new Set() const results = [] for (const taskItem of taskList) { const taskPromise = Promise.resolve().then(() => cluster.execute(taskItem)) results.push(taskPromise) // 任务执行完就从执行集合里移除 taskPromise.finally(() => executing.delete(taskPromise)) executing.add(taskPromise) // 当执行中的任务数到达上限,就等任意一个任务完成再继续塞新任务 if (executing.size >= concurrencyLimit) { await Promise.race(executing) } } // 等所有任务跑完,统一返回结果 await Promise.all(results) return results } // 调用方法,保持5个并发,同时拿到每个任务的返回值 await runWithConcurrency(urls, 5)
内容的提问来源于stack exchange,提问作者user2912391
相关产品推荐
相关产品推荐

