You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS中如何并行运行多个相同脚本实例且互不干扰

方案选型结论

你提到的两个方案都不匹配当前场景:

  • PM2的核心能力是常驻进程守护、静态多实例负载均衡,不支持按需动态拉起一次性任务进程,硬适配会出现进程残留、资源泄漏、管理逻辑复杂的问题。
  • Node.js原生Cluster模块的设计目标是榨取多核CPU性能做服务负载均衡,工作进程都是常驻模型,用来做动态一次性爬虫任务需要自己从零实现进程生命周期管理、异常回收、通信逻辑,性价比极低。
推荐落地方案

采用动态fork独立子进程 + 并发控制的实现方式,完全满足“任务互相隔离、按需启动、不影响已有任务”的核心需求,改造成本极低。
具体实现步骤如下:

  • 拆分独立爬虫执行脚本
    把后端爬虫逻辑从现有前端静态资源目录里抽离,在项目根目录新建tasks文件夹存放后台任务,爬虫脚本单独放在tasks/scrape-task.js中。脚本只负责接收主进程传入的用户表单参数,启动Puppeteer实例执行爬取逻辑,执行结束(成功/失败/超时)后将结果回传给主进程,随后自动退出。
    注意脚本内必须写全资源回收逻辑:无论任务执行结果如何,都要调用browser.close()关闭Chrome实例;启动Puppeteer时可以关闭非必要功能(比如默认开启的图片加载、沙箱、扩展程序),降低单任务内存占用。
  • Express接口层动态拉起独立子进程
    在表单提交对应的路由处理逻辑中,不要直接在Express服务线程内执行爬虫逻辑,每次收到合法请求就通过Node.js内置的child_process.fork拉起独立的爬虫子进程:
    const { fork } = require('child_process');
    const path = require('path');
    
    // 表单提交路由
    router.post('/start-scrape', (req, res) => {
      const userFormParams = req.body;
      // 拉起独立爬虫进程,进程间完全隔离
      const taskProcess = fork(path.resolve(__dirname, '../tasks/scrape-task.js'));
      // 向子进程传递用户提交的参数
      taskProcess.send({ formData: userFormParams });
    
      // 接收子进程回传的爬取结果
      taskProcess.on('message', (taskResult) => {
        res.json({ success: true, data: taskResult });
      });
    
      // 监听进程退出事件,处理异常场景
      taskProcess.on('exit', (exitCode) => {
        if (exitCode !== 0 && !res.headersSent) {
          res.json({ success: false, msg: '爬取任务执行异常' });
        }
      });
    });
    
    这种模式下每个爬虫任务都运行在独立的操作系统进程中,单个任务崩溃、卡死完全不会影响其他正在运行的任务,也不会阻塞主Express服务的正常请求处理,进程执行完毕后会自动被系统回收,不会残留冗余资源。
  • 增加并发控制避免服务器过载
    Puppeteer启动的Chrome实例单实例内存占用在80M-150M左右,无限制拉起进程会直接导致服务器内存耗尽OOM。你需要引入轻量任务队列(比如p-queue)控制最大并发数,参考服务器配置设置阈值:2核4G服务器建议最大并发设为3-5,超出阈值的请求进入队列等待,有任务执行完毕释放资源后再启动新任务。
    额外需要给每个任务设置超时时间(比如单任务最长执行5分钟),超时后直接kill对应进程,避免死锁、页面卡死导致的资源占用。
  • 长任务适配(可选)
    如果单爬虫任务执行时间较长,不适合让前端持续等待接口响应,可以给每个启动的任务生成唯一ID,将任务状态(排队中/执行中/成功/失败、结果数据)存在内存或Redis中,前端通过轮询或WebSocket拉取任务状态即可。
适配后的目录结构

调整后的结构完全兼容你现有项目,只需要新增任务目录即可:

my-app/
├─ bin/
│  ├─ www
├─ node_modules/
├─ public/
│  ├─ images/
│  ├─ javascript/
│  │  ├─ scrapper.js  // 保留为前端侧脚本,后端爬虫逻辑迁移到tasks目录
│  │  ├─ script.js
│  ├─ stylesheets/
├─ routes/
│  ├─ index.js
├─ tasks/  // 新增:存放所有后台执行的任务脚本
│  ├─ scrape-task.js
├─ views/
│  ├─ error.pug
│  ├─ index.pug
│  ├─ layout.pug
├─ app.json
├─ package-lock.json
├─ package.json

如果后续业务量上涨,单台服务器无法承载并发,只需要把任务投递逻辑替换为消息队列(比如BullMQ),将爬虫脚本拆为独立的Worker节点横向扩展即可,核心逻辑不需要大幅改动。

内容的提问来源于stack exchange,提问作者Nogaruki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 18:48:28