使用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);
相关推荐
花花鱼3 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k09337 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
EricWang135828 分钟前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning28 分钟前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人38 分钟前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
番茄小酱00140 分钟前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js
子非鱼9211 小时前
【Ajax】跨域
javascript·ajax·cors·jsonp
超雄代码狂1 小时前
ajax关于axios库的运用小案例
前端·javascript·ajax
长弓三石1 小时前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙
小马哥编程1 小时前
【前端基础】CSS基础
前端·css