我该使用worker还是child process运行可按pid终止的puppeteer函数?
方案选型结论
- 优先选用**Child Process(子进程)**实现,不要使用Worker Thread,原因如下:
- Worker Thread属于同一主进程下的多线程,所有线程共享同一个进程PID,无法实现按独立PID终止指定任务的需求
- 子进程拥有独立的PID、内存空间,Puppeteer实例崩溃不会影响主进程运行,完全符合独立隔离、单独终止的要求
process.kill(child.pid)完全可以实现终止指定子进程的操作,只要提前存储每个子进程的PID和实例映射关系即可。
前置依赖安装
先安装所需依赖:npm install puppeteer
1. job.js 代码(子进程执行文件)
const puppeteer = require('puppeteer'); let browser = null; // 子进程启动后自动运行Puppeteer实例 (async () => { try { browser = await puppeteer.launch({ headless: 'new', // 此处可添加你需要的其他puppeteer配置 }); // 通知主进程启动成功,携带当前子进程PID process.send({ type: 'start_success', pid: process.pid, browserWSEndpoint: browser.wsEndpoint() }); // 此处添加你需要的Puppeteer业务逻辑 // 示例:打开百度 const page = await browser.newPage(); await page.goto('https://www.baidu.com'); console.log('页面加载完成,子进程PID:', process.pid); } catch (err) { process.send({ type: 'error', pid: process.pid, msg: err.message }); process.exit(1); } })(); // 监听主进程的终止信号,先关闭浏览器再退出,避免残留Chrome进程 process.on('message', async (msg) => { if (msg.type === 'stop') { if (browser) await browser.close(); process.exit(0); } }); // 子进程退出前强制关闭浏览器,防止资源残留 process.on('exit', async () => { if (browser) await browser.close(); });
2. main.js 代码(主进程,带启动/停止逻辑)
const { fork } = require('child_process'); const path = require('path'); // 存储子进程映射:key为pid,value为子进程实例 const childProcessMap = new Map(); // 启动按钮点击触发的函数 function startJob() { // fork创建子进程执行job.js const child = fork(path.join(__dirname, 'job.js')); // 存储子进程实例 childProcessMap.set(child.pid, child); console.log('启动子进程,PID:', child.pid); // 监听子进程消息 child.on('message', (data) => { if (data.type === 'start_success') { console.log(`子进程${data.pid}的Puppeteer启动成功`); } else if (data.type === 'error') { console.log(`子进程${data.pid}报错:`, data.msg); childProcessMap.delete(data.pid); } }); // 监听子进程退出,清除映射避免内存泄漏 child.on('exit', (code) => { console.log(`子进程${child.pid}退出,退出码:`, code); childProcessMap.delete(child.pid); }); return child.pid; } // 停止按钮点击触发的函数,传入要停止的子进程pid function stopJob(pid) { const child = childProcessMap.get(pid); if (!child) { console.log(`不存在PID为${pid}的子进程`); return false; } // 先发送优雅停止信号,让子进程主动关闭浏览器再退出 child.send({ type: 'stop' }); // 3秒后如果子进程仍未退出,强制杀进程 setTimeout(() => { if (childProcessMap.has(pid)) { try { process.kill(pid, 'SIGKILL'); childProcessMap.delete(pid); console.log(`强制杀死子进程${pid}成功`); } catch (err) { console.log(`杀死子进程${pid}失败:`, err.message); childProcessMap.delete(pid); } } }, 3000); return true; } // 测试代码:启动后10秒自动停止 // const testPid = startJob(); // setTimeout(() => { // stopJob(testPid); // }, 10000); // 如果你是GUI场景,将startJob和stopJob绑定到对应按钮的点击事件即可
注意事项
- 杀死子进程前优先触发Puppeteer的browser关闭逻辑,避免残留孤立的Chrome进程占用系统资源,上述代码已经做了相关处理
- 每次点击启动按钮调用一次
startJob()即可创建独立的Puppeteer实例,每个实例都有独立PID,互不影响 - Windows系统下极少数场景
process.kill可能无法杀全进程树,可额外引入tree-kill包处理
内容的提问来源于stack exchange,提问作者noobcode0000
相关产品推荐
相关产品推荐

