Node.js page.goto并发数超预期问题排查及正确实现咨询
问题排查与修复方案
问题根源
你的代码出现并发数超限的核心原因是竞态条件:
当多个请求同时进入page.goto函数时,它们会同时执行if(this.pool.requestingPages > 30)的判断。假设此时requestingPages的值是30,所有这些请求都会判定条件不成立,直接跳过等待逻辑,紧接着执行requestingPages++,导致请求数瞬间突破30的限制,甚至达到35。
除此之外,代码还有两个次要问题:
- 等待队列使用
pop()唤醒请求,会导致后进先出的处理顺序,不符合常规的请求排队逻辑。 - 成功和失败分支重复了相同的清理代码,冗余且容易出错。
修复后的并发控制代码
// 先保存原始的page.goto方法 const originalPageGoto = page.goto; page.goto = async function(url, options) { // 循环等待,直到有可用的并发槽位 while (this.pool.requestingPages >= 30) { await new Promise(resolve => this.pool.waitList1.push(resolve)); } // 占用一个并发槽位 this.pool.requestingPages++; let result; try { // 执行原始的goto逻辑 result = await originalPageGoto.call(this, url, options); } catch (e) { // 包装错误信息,便于排查 throw new Error(`goto error: ${e.message}`); } finally { // 释放并发槽位 this.pool.requestingPages--; // 唤醒等待队列中的第一个请求(先进先出) if (this.pool.waitList1.length > 0) { const resolve = this.pool.waitList1.shift(); resolve(); } } return result; };
关键优化点
- 避免竞态条件:通过
while循环检查并发数,确保只有当requestingPages低于30时,才会占用槽位,从根源上防止并发数超限。 - 先进先出的队列处理:使用
shift()从队列头部取出唤醒函数,保证请求按进入等待队列的顺序被处理。 - 统一清理逻辑:用
finally块处理请求完成后的槽位释放和队列唤醒,避免代码冗余。 - 错误信息增强:在抛出错误时携带原始错误信息,便于后续排查具体问题。
内容的提问来源于stack exchange,提问作者dowson killy
相关产品推荐
相关产品推荐

