使用NodeJS仿写pm2

闲来无事,使用nodejs中的模块中的方法仿写pm2构建了一个简单的进程管理器(SimplePM),本文记录了仿写过程中的重要步骤:

  1. 构建启动方法(start):start 方法中,我们使用 child_process 模块的 spawn 方法来启动一个新的 Node.js 进程。我们将脚本名称作为参数传递给 spawn 方法,并将返回的子进程对象存储在 this.processes 对象中。然后,我们使用子进程对象的事件处理程序来监听子进程的输出和关闭事件。
  2. 构建停止方法(stop):stop 方法中,我们检查指定脚本名称是否存在于 this.processes 对象中,如果存在则调用 kill 方法杀死对应的子进程。然后从 this.processes 对象中删除对应的脚本。
  3. 构建重启方法(restart):restart 方法中,我们首先调用 stop 方法停止指定脚本的运行,然后调用 start 方法重新启动该脚本。这样就实现了重启功能。
  4. 构建暂停方法(pause):pause 方法中,我们检查指定脚本名称是否存在于 this.processes 对象中,如果存在则向子进程发送 SIGSTOP 信号以暂停其执行。这将使子进程暂时停止,直到接收到 SIGCONT 信号恢复执行。
  5. 构建恢复方法(resume):resume 方法中,我们检查指定脚本名称是否存在于 this.processes 对象中,如果存在则向子进程发送 SIGCONT 信号以恢复其执行。
  6. 构建列表方法(list):list 方法中,我们使用 console.log 打印当前被管理的脚本。

下面是完整的 SimplePM 类的代码,已经添加了注释,并使用了Markdown(md)格式:

markdown 复制代码
# SimplePM

`SimplePM` 是一个用于管理Node.js应用程序的简单进程管理器。

## 构造函数

```javascript
const { spawn } = require('child_process');

class SimplePM {
  constructor() {
    this.processes = {};
  }

启动方法(start)

javascript 复制代码
  start(script) {
    // 使用子进程模块的 spawn 方法启动新的 Node.js 进程
    const proc = spawn('node', [script]);

    // 将子进程对象存储在 processes 对象中
    this.processes[script] = proc;

    console.log(`Started ${script} with pid: ${proc.pid}`);

    // 监听子进程的输出(stdout)
    proc.stdout.on('data', (data) => {
      console.log(`${script} stdout:\n${data}`);
    });

    // 监听子进程的错误输出(stderr)
    proc.stderr.on('data', (data) => {
      console.error(`${script} stderr:\n${data}`);
    });

    // 监听子进程的关闭事件
    proc.on('close', (code) => {
      console.log(`${script} process exited with code ${code}`);
      delete this.processes[script];
    });
  }

停止方法(stop)

javascript 复制代码
  stop(script) {
    // 检查指定脚本是否存在于 processes 对象中
    if (!this.processes[script]) {
      console.log(`No such process for script: ${script}`);
      return;
    }

    console.log(`Stopping ${script}`);
    // 杀死对应的子进程
    this.processes[script].kill();

    // 从 processes 对象中删除对应的脚本
    delete this.processes[script];
  }

重启方法(restart)

javascript 复制代码
  restart(script) {
    // 先停止指定脚本,然后重新启动它
    this.stop(script);
    this.start(script);
  }

暂停方法(pause)

javascript 复制代码
  pause(script) {
    // 检查指定脚本是否存在于 processes 对象中
    if (!this.processes[script]) {
      console.log(`No such process for script: ${script}`);
      return;
    }

    console.log(`Pausing ${script}`);
    // 向子进程发送 pause 信号暂停执行
    this.processes[script].stdin.write('pause\n');
    // unix中:this.processes[script].kill('SIGSTOP');
  }

恢复方法(resume)

javascript 复制代码
  resume(script) {
    // 检查指定脚本是否存在于 processes 对象中
    if (!this.processes[script]) {
      console.log(`No such process for script: ${script}`);
      return;
    }

    console.log(`Resuming ${script}`);
    // 向子进程发送 resume 信号恢复执行
    this.processes[script].stdin.write('resume\n');
    // unix中:this.processes[script].kill('SIGCONT');
  }

列表方法(list)

javascript 复制代码
  list() {
    console.log('Currently managed scripts: ', Object.keys(this.processes));
  }
}

module.exports = SimplePM;

SimplePM的完整代码

js 复制代码
// simplePM.js
const {
  spawn
} = require('child_process');

class SimplePM {
  constructor() {
    this.processes = {};
  }

  start(script) {
    // 使用子进程模块的 spawn 方法启动新的 Node.js 进程
    const proc = spawn('node', [script]);

    // 将子进程对象存储在 processes 对象中
    this.processes[script] = proc;

    console.log(`Started ${script} with pid: ${proc.pid}`);

    // 监听子进程的输出(stdout)
    proc.stdout.on('data', (data) => {
      console.log(`${script} stdout:\n${data}`);
    });

    // 监听子进程的错误输出(stderr)
    proc.stderr.on('data', (data) => {
      console.error(`${script} stderr:\n${data}`);
    });

    // 监听子进程的关闭事件
    proc.on('close', (code) => {
      console.log(`${script} process exited with code ${code}`);
      delete this.processes[script];
    });
  }

  stop(script) {
    // 检查指定脚本是否存在于 processes 对象中
    if (!this.processes[script]) {
      console.log(`No such process for script: ${script}`);
      return;
    }

    console.log(`Stopping ${script}`);
    // 杀死对应的子进程
    this.processes[script].kill();

    // 从 processes 对象中删除对应的脚本
    delete this.processes[script];
  }

  restart(script) {
    // 先停止指定脚本,然后重新启动它
    this.stop(script);
    this.start(script);
  }

  pause(script) {
    // 检查指定脚本是否存在于 processes 对象中
    if (!this.processes[script]) {
      console.log(`No such process for script: ${script}`);
      return;
    }

    console.log(`Pausing ${script}`);
    // 向子进程发送 SIGSTOP 信号暂停执行
    // this.processes[script].kill('SIGSTOP');
    // this.processes[script].stdin.pause(); // 只能通过信号的方式控制子进程的启停
    this.processes[script].stdin.write('pause\n');
  }

  resume(script) {
    // 检查指定脚本是否存在于 processes 对象中
    if (!this.processes[script]) {
      console.log(`No such process for script: ${script}`);
      return;
    }

    console.log(`Resuming ${script}`);
    // 向子进程发送 SIGCONT 信号恢复执行
    this.processes[script].stdin.write('resume\n');
    // this.processes[script].stdin.resume(); // 只能通过信号的方式控制子进程的启停
    // this.processes[script].kill('SIGCONT');
  }
  list() {
    console.log('Currently managed scripts: ', Object.keys(this.processes));
  }
};

// 进程管理器的实例化
const pm = new SimplePM();
// 使用子进程启动
pm.start('./hello.js');
// 罗列管理的进程
pm.list();
// 三秒之后暂停子进程
setTimeout(()=>{
  pm.pause('./hello.js');
  // 再过三秒重新启动子进程
  setTimeout(()=>{
    pm.resume('./hello.js');
  }, 3000);
}, 3000);

测试代码

js 复制代码
// hello.js
const fs = require('fs');

process.stdin.setEncoding('utf-8');

// 监听来自父进程的输入
process.stdin.on('data', (data) => {
  const message = data.trim();

  if (message === 'pause') {
    // 执行暂停操作
    // 可以在此处添加相关逻辑
    fs.appendFileSync('./log.txt', '子进程已暂停\n');
  } else if (message === 'resume') {
    // 执行恢复操作
    // 可以在此处添加相关逻辑
    fs.appendFileSync('./log.txt', '子进程已恢复\n');
  }
});

// 写一个回调地狱
setTimeout(() => {
  fs.appendFileSync('./log.txt', 'hello1\n');
  setTimeout(() => {
    fs.appendFileSync('./log.txt', 'hello2\n');
    setTimeout(() => {
      fs.appendFileSync('./log.txt', 'hello3\n');
      setTimeout(() => {
        fs.appendFileSync('./log.txt', 'hello4\n');
        setTimeout(() => {
          fs.appendFileSync('./log.txt', 'hello5\n');
          setTimeout(() => {
            fs.appendFileSync('./log.txt', 'hello6\n');
            setTimeout(() => {
              fs.appendFileSync('./log.txt', 'hello7\n');
            }, 1000);
          }, 1000);
        }, 1000);
      }, 1000);
    }, 1000);
  }, 1000);
}, 1000);
相关推荐
海上彼尚16 分钟前
Nodejs也能写Agent - 16.LangGraph篇 - 条件分支与循环
前端·后端·langchain·node.js
To_OC1 小时前
从 “卡死半天” 到 “打字机效果”:大模型流式输出前端实现全记录
前端·javascript·llm
rockey6271 小时前
基于AScript的JavaScript脚本语言发布啦
javascript·c#·.net·js·script
SoaringHeart2 小时前
Flutter最佳实践:键盘辅助视图输入框天添加图片附件
前端·flutter
kyriewen2 小时前
我看完这篇安全论文——AI推荐的npm包,92%是编出来的
前端·javascript·ai编程
心中有国也有家3 小时前
AtomGit Flutter 鸿蒙客户端:Canvas 绘制进阶-路径、渐变与混合模式
android·javascript·flutter·华为·harmonyos
小心我捶你啊3 小时前
数据采集和Web解锁不是一回事,从用途到规则区分
前端·爬虫·网络协议
hunterandroid4 小时前
[鸿蒙从零到一] ArkUI 基础组件实战:Text、Image、Button 与 TextInput
前端
fsssb4 小时前
Chromium 源码学习笔记(五):一次点击,在进入 JS 之前先经历了什么?
前端