Node.js中child_process实现多进程

下面是详细讲解“Node.js中child_process实现多进程”的完整攻略。

一、什么是child_process模块

在Node.js中,使用child_process模块可以创建并控制子进程。这个模块提供了四个函数:spawn、exec、execFile、fork,分别对应不同类型的子进程。

二、何时使用多进程

在一些需要高并发处理的场景中,单进程的性能可能会遇到瓶颈,此时可以考虑使用多进程来提高性能。

三、使用child_process实现多进程的步骤

下面以创建多个子进程来同时处理请求为例,讲解如何使用child_process模块实现多进程。

1. 启动子进程

具体的代码如下所示:

const cp = require('child_process');
const numCPUs = require('os').cpus().length;

for (let i = 0; i < numCPUs; i++) {
  const child = cp.fork('./worker.js');
  console.log(`Started worker ${child.pid}`);
}

以上代码会启动和CPU数量相等的子进程,每个子进程执行的脚本为worker.js

2. 子进程执行任务

子进程在启动后,会执行worker.js脚本中的代码。在worker.js中,可以监听主进程发送的消息,并根据不同的消息类型执行不同的任务。具体的代码如下所示:

process.on('message', ({ type, payload }) => {
  switch (type) {
    case 'TASK_A':
      doTaskA(payload);
      break;
    case 'TASK_B':
      doTaskB(payload);
      break;
    default:
      break;
  }
});

function doTaskA(payload) {
  // 处理任务A
  process.send({ type: 'TASK_A_RESULT', payload: result });
}

function doTaskB(payload) {
  // 处理任务B
  process.send({ type: 'TASK_B_RESULT', payload: result });
}

上述代码中,process.on('message')用于监听主进程发送的消息,根据不同的消息类型执行不同的任务。任务执行完毕后,会通过process.send方法向主进程返回执行结果。

3. 主进程发送任务

主进程中可以根据需要将不同类型的任务分发给子进程执行,具体的代码如下所示:

const workers = [];

for (let i = 0; i < numCPUs; i++) {
  const child = cp.fork('./worker.js');
  console.log(`Started worker ${child.pid}`);
  workers.push(child);
}

workers.forEach((worker, index) => {
  worker.send({ type: 'TASK_A', payload: `Task A for worker ${index}` });
  worker.send({ type: 'TASK_B', payload: `Task B for worker ${index}` });
});

以上代码会将TASK_ATASK_B两种任务分发给每个子进程执行。每个任务的payload可以根据需要进行设置。

四、使用示例

下面分别提供两个使用child_process实现多进程的示例:

示例一:使用child_process处理大文件

以下代码会使用两个子进程,分别对两个大文件进行排序,并将结果写入新的文件中。

const fs = require('fs');
const path = require('path');
const cp = require('child_process');
const util = require('util');

const numCPUs = require('os').cpus().length;
const inFile = path.join(__dirname, 'bigfile.txt');
const outFile = path.join(__dirname, 'output.txt');

let lineCount = 0;

const readStream = fs.createReadStream(inFile);
readStream.on('data', chunk => {
  const lines = chunk.toString().split('\n');
  lineCount += lines.length;

  // 分配给每个子进程的行数
  const chunkSize = Math.ceil(lines.length / numCPUs);

  for (let i = 0; i < numCPUs; i++) {
    const start = chunkSize * i;
    const end = start + chunkSize;
    const child = cp.fork(path.join(__dirname, 'child.js'));
    const subLines = lines.slice(start, end);
    const payload = {
      inFile,
      outFile,
      subLines
    };
    child.send(payload);
    console.log(`Forked process with PID ${child.pid}.`);
  }
});

readStream.on('end', () => {
  console.log(`There are ${lineCount} lines in total.`);
});

let processedCount = 0;

function messageHandler(childProcess) {
  return function (message) {
    processedCount++;
    if (message.type === 'done') {
      console.log(`Process with PID ${childProcess.pid} is done.`);
    }
    if (processedCount === numCPUs) {
      console.log(`All processes are done. Start merging files.`);
      mergeFiles(numCPUs);
    }
  };
}

function mergeFiles(count) {
  const readerStreams = [];
  for (let i = 0; i < count; i++) {
    const reader = fs.createReadStream(
      path.join(__dirname, `part-${i}.txt`),
      { encoding: 'utf-8' }
    );
    readerStreams.push(reader);
  }
  let writeStream = fs.createWriteStream(outFile, { encoding: 'utf-8' });
  util.promisify(writeStream.on).call(writeStream, 'finish', () => {
    console.log(`Merging files is done.`);
  });

  let index = 0;

  function writeNext() {
    const reader = readerStreams.shift();
    if (reader) {
      reader.pipe(writeStream, { end: false });
      reader.once('end', () => {
        console.log(`Finished writing file ${index}.`);
        index++;
        writeNext();
      });
    }
  }

  writeNext();
}

子进程的代码如下:

const fs = require('fs');
const path = require('path');

process.on('message', message => {
  const { inFile, outFile, subLines } = message;
  subLines.sort();
  const content = subLines.join('\n');
  fs.writeFile(path.join(__dirname, `part-${process.pid}.txt`), content, err => {
    if (err) {
      process.send({ type: 'error', message: err.message });
    } else {
      process.send({ type: 'done' });
    }
    process.exit();
  });
});

示例二:使用child_process处理CPU密集型计算

以下代码会使用多个子进程,计算质数。主进程会将计算任务分发给子进程并进行计算结果的汇总。

const cp = require('child_process');
const numCPUs = require('os').cpus().length;

let idx = 1;

for (let i = 0; i < numCPUs; i++) {
  const child = cp.fork('./worker.js');
  child.on('message', msg => {
    console.log(`Worker ${msg.workerId} calculated ${msg.count} prime numbers.`);
    idx++;
    if (idx > 100) {
      console.log(`All workers are done.`);
      process.exit();
    }
  });
  const payload = {
    workerId: i,
    start: i * 100000,
    end: (i + 1) * 100000 - 1
  };
  child.send(payload);
  console.log(`Forked worker ${child.pid}.`);
}

上述代码中,主进程会启动和CPU数量相等的子进程,每个子进程执行的代码为worker.js。主进程将需要计算的数值范围分配给每个子进程,并等待每个子进程返回计算结果。

子进程的代码如下所示:

process.on('message', msg => {
  const { start, end } = msg;
  let count = 0;
  for (let i = start; i <= end; i++) {
    if (isPrime(i)) {
      count++;
    }
  }
  process.send({ count, workerId: msg.workerId });
  process.exit();
});

function isPrime(num) {
  for (let i = 2; i <= Math.sqrt(num); i++) {
    if (num % i === 0) {
      return false;
    }
  }
  return num !== 1;
}

上述代码中,子进程会接收到主进程分配的数值范围,并计算出这个范围内的质数个数。计算完毕后,子进程通过process.send方法向主进程返回计算结果。

以上就是使用child_process模块实现多进程的完整攻略和两个示例。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Node.js中child_process实现多进程 - Python技术站

(0)
上一篇 2023年6月8日
下一篇 2023年6月8日

相关文章

  • vue.js diff算法原理详细解析

    Vue.js Diff算法原理详细解析 什么是Vue.js的Diff算法? Vue.js是一个基于组件化的视图框架,它通过数据驱动视图的更新。在这个过程中,Vue会对比新旧虚拟DOM树间的差异,并且仅仅更新有变化的DOM元素。而这个通过比较两个虚拟DOM树之间的差异,找到需要更新的节点的过程,我们称之为Vue.js的Diff算法。 Vue.js 2.x中的D…

    node js 2023年6月8日
    00
  • 简单的socket编程入门示例

    下面是详细的“简单的socket编程入门示例”的攻略: 什么是Socket编程 Socket编程是一种基于网络通信协议的编程方式,它可以让程序在网络中传输数据。Socket编程是建立于TCP/IP协议之上的,使用Socket编程可以实现一些网络应用程序,如HTTP、FTP、SMTP等。 Socket编程的基本步骤 Socket编程的基本步骤如下: 创建Soc…

    node js 2023年6月8日
    00
  • 通过nodejs 服务器读取HTML文件渲染到页面的方法

    首先,要实现通过nodejs服务器读取HTML文件并将其渲染到页面,我们需要用到Node.js的http、fs和path模块。 创建nodejs服务器 首先,在你的项目目录下创建一个server.js文件,使用以下代码创建一个简单的http服务器: const http = require(‘http’); http.createServer(functio…

    node js 2023年6月8日
    00
  • 教你彻底搞懂ESM与CJS互相转换

    教你彻底搞懂ESM与CJS互相转换 在讲解转换之前,我们需要先了解ESM(ECMAScript Modules)和CJS(CommonJS Modules)的基本概念。 ESM和CJS概念 ESM ESM是一种ECMAScript规范化模块格式,它是ECMAScript 6标准中引入的,它使用import/export关键字进行模块的导入/导出。 示例代码:…

    node js 2023年6月9日
    00
  • Express 框架中使用 EJS 模板引擎并结合 silly-datetime 库进行日期格式化的实现方法 原创

    下面是如何在 Express 框架中使用 EJS 模板引擎并结合 silly-datetime 库进行日期格式化的实现方法,分为以下两个步骤: 步骤一:安装和配置 1. 安装 Express 和 EJS 在项目根目录中运行以下命令来安装 Express 和 EJS: npm install express ejs –save 2. 安装 silly-dat…

    node js 2023年6月8日
    00
  • Node.JS如何实现JWT原理

    JWT(JSON Web Token)是一种开放标准,它允许将信息作为 JSON 对象进行安全地传输。JWT 在身份验证和授权应用程序中得到广泛使用。Node.js 提供了各种库和模块来实现 JWT,包括 jsonwebtoken 和 express-jwt。下面是使用 Node.js 实现 JWT 的过程步骤。 步骤1:安装所需库 首先,我们需要安装所需的…

    node js 2023年6月8日
    00
  • 详解nodeJs文件系统(fs)与流(stream)

    下面是对Node.js文件系统(fs)和流(stream)的详解攻略。 fs模块的介绍 Node.js的fs模块提供了一组丰富的API用于文件系统操作,包括文件的读取、写入、修改、删除等。该模块使用同步或异步的方式访问文件系统,可以操作各种类型的文件,包括文本、图片、视频、音频等。 fs的常见API 以下是一些最常用的fs API: 读取文件: fs.rea…

    node js 2023年6月8日
    00
  • StringUtils工具包中字符串非空判断isNotEmpty和isNotBlank的区别

    StringUtils工具包是Apache Commons Lang中提供的一个字符串处理工具类库。其中,isNotEmpty和isNotBlank是用来判断字符串是否为空的两个方法,它们的区别如下: isNotEmpty isNotEmpty方法用于判断字符串是否不为空,不为空的条件是字符串不为null且长度大于0。 StringUtils.isNotEm…

    node js 2023年6月8日
    00
合作推广
合作推广
分享本页
返回顶部