Nodejs-child_process模块解读

news/2024/11/16 17:37:32/文章来源:https://www.cnblogs.com/miniwa/p/18407654

在 Node.js 应用程序中,child 进程模块非常重要,有了它可以实现并行处理,这在资源密集型任务里十分重要。

在本文中,我们将看一下 child 进程模块,解释其目的、使用方式以及如何使用。

什么是子进程模块

子进程是核心模块,允许用户创建和控制子进程。这些进程可以执行系统命令、运行各种语言的脚本,甚至可以创建新的 Node.js 实例。

child 进程模块的主要目的是允许同时执行多个进程,而不会阻塞主事件循环。

对于需要处理 CPU 密集型操作或执行外部命令和脚本的应用来说,使用 child 进程可以确保应用的高性能和响应性。

子进程的使用方式

child 进程可以用于以下任务:

  • 并行处理Child 处理通过允许应用程序将工作负载分布在多个 CPU 核心上,从而显著提高 CPU 密集型活动(如图像处理和数据分析)的性能。

  • 运行 shell 脚本Child 进程可以用来执行 shell 脚本。您可以使用 exec 技术来运行 shell 命令并捕获它们的输出,还可以使用 spawn 方法,当直接运行脚本时提供更大的控制。

  • 与其他服务通信:在通信方面,child 进程起着至关重要的作用。它可以与外部服务(如数据库、API 或微服务)进行通信。它们可以用来调用外部 API,执行数据库查询,与其它微服务通信。

创建子进程

要创建一个 child 进程,Node.js 为我们提供了四种主要方法来创建一个 child 进程,分别是 exec(), execFile(), spawn(), fork()

exec() 方法

exec() 方法在 shell 中运行一个命令并缓冲输出。它适用于运行 shell 命令并记录输出。但由于是缓冲,它有内存限制。

下面是一个使用 exec() 方法执行命令并获取和处理一些系统信息的例子。

const { exec } = require("child_process");exec("df -h", (error, stdout, stderr) => {if (error) {console.error(`exec error: ${error.message}`);return;}if (stderr) {console.error(`stderr: ${stderr}`);return;}const lines = stdout.trim().split("\n");const diskInfo = lines[1].split(/\s+/);const totalSpace = diskInfo[1];const usedSpace = diskInfo[2];const availableSpace = diskInfo[3];const usagePercent = diskInfo[4];console.log(`Total Space: ${totalSpace}`);console.log(`Used Space: ${usedSpace}`);console.log(`Available Space: ${availableSpace}`);console.log(`Usage: ${usagePercent}`);
});

这将执行 df -h 命令,解析其输出,并显示总的、已用的和可用的磁盘空间以及使用百分比。

运行代码时,您应该会看到类似这样的内容:

img

execFile() 方法

execFile() 方法在没有 shell 的情况下运行可执行文件。它比 exec() 更高效,因为它避免了 shell 的开销。

下面是一个如何使用 execFile() 方法的例子:

const { execFile } = require('child_process');
execFile("node", ["--version"], (error, stdout, stderr) => {if (error) {console.error(`execFile error: ${error.message}`);return;}if (stderr) {console.error(`stderr: ${stderr}`);return;}console.log(`stdout: ${stdout}`);
});

这个例子展示了运行 Node.js 可执行文件来获取其版本。当命令不需要 shell 特性时,推荐使用 execFile()

spawn() 方法

与主要用于执行 shell 命令或可执行文件并捕获其输出的 exec 方法不同,spawn() 方法通过基于流的输出处理、直接与可执行文件交互、事件驱动的通信,提供了对 child 进程的更多控制。

spawn() 方法可用于需要直接交互的复杂场景。spawn() 启动一个新进程,给定一个命令,并提供流 i.e stdout, stderr 用于直接处理进程的输出和错误。

下面是一个如何使用 spawn() 方法的例子

const { spawn } = require("child_process");const ls = spawn("ls", ["-lh", "/usr"]);ls.stdout.on("data", (data) => {console.log(`stdout: ${data}`);
});ls.stderr.on("data", (data) => {console.error(`stderr: ${data}`);
});ls.on("close", (code) => {console.log(`child process exited with code ${code}`);
});

在这个例子中,spawn() 运行 ls 命令并附加事件侦听器来处理进程的输出和退出状态。

运行代码时,您应该会看到类似这样的内容:

img

fork() 方法

fork() 方法可以说是为创建新的 Node.js 进程而设计的 spawn() 的变体。与可以启动任何类型进程的 spawn() 不同,fork() 方法针对创建本身是 Node.js 应用程序的 child 进程进行了优化。

它为 child 进程提供了一个额外的通信通道,允许在父进程和 child 进程之间进行简单的消息传输。

下面是一个如何使用 fork() 方法的例子。

父文件,即 parent.js

const { fork } = require("child_process");const child = fork("child.js");child.on("message", (message) => {console.log(`Message from child: ${message}`);
});child.send("Hello, child process!");

child 进程文件,即 child.js

process.on("message", (message) => {console.log(`Message from parent: ${message}`);process.send("Hello from child process!");
});

在这里,fork() 创建了一个运行 child.js 脚本的新 Node.js 进程。它允许使用 send() 和 message 事件在父进程和子进程之间传递消息。

当您运行代码时,您应该会看到类似这样的内容:

img

fork()spawn() 之间的区别

forkspawn 之间的一些区别包括:

  • 内置通信通道fork() 在父进程和 child 进程之间自动设置了一个 IPC(进程间通信)通道。相比之下,spawn() 默认不建立这个通道;如果需要,开发人员必须手动配置 IPC。

  • 隔离和独立性:每个由 fork() 生成的 child 进程都是一个单独的 Node.js 进程,拥有自己的 V8 实例。另一方面,spawn() 方法可以启动任何进程,包括 Node.js 应用程序,并不提供相同级别的隔离。

  • 用例特异性fork() 技术专门用于需要生成工作进程的情况,这些工作进程是同一应用程序的一部分,但同时运行。相比之下,spawn() 更为通用,用于需要启动 Node.js 生态系统之外的任意命令或脚本时。

处理子进程输出

当创建一个 child 进程时,它会在两个主要流中产生输出:stdoutstderr。这些流保存了由 child 进程运行的命令或脚本的结果。要收集此输出,请为 child 进程对象添加事件侦听器,以在 stdoutstderr 上的 data 事件上。

Node.js 提供了几种特别有用的事件侦听器,用于处理 child 进程输出和错误。其中包括:

  • Data:数据事件由代表子进程的 stdout 和 stderr 的流发出。

  • Error
    child 进程执行过程中发生错误时,会触发错误事件。这可能是由于命令本身的问题,或者是读取 stdoutstderr 时出现问题。

  • Close:当 child 进程结束时,会发出关闭事件,表明它是否成功终止或被杀死。

让我们看一个示例,展示如何生成一个 child 进程,捕获其输出,并处理可能发生的任何错误。

我们将使用 spawn() 方法来启动一个简单的命令(echo)并侦听输出和错误。

const { spawn } = require("child_process");const child = spawn("echo", ["Hello, world"]);child.stdout.on("data", (data) => {console.log(`stdout: ${data}`);
});child.stderr.on("data", (data) => {console.error(`stderr: ${data}`);
});child.on("error", (error) => {console.error(`Error occurred: ${error.message}`);
});child.on("close", (code) => {console.log(`Child process exited with code ${code}`);
});

在这个例子中,父进程生成一个 child 进程来执行 echo 命令,该命令简单地将 Hello, world! 打印到终端。

父进程通过附加到 stdoutstderrdata 事件侦听器来捕获此输出。此外,它还侦听 error 事件以捕获在执行 child 进程期间可能发生的任何错误。

最后,close 事件指示 child 进程已完成执行,以及退出代码,这可以用来确定进程是否成功完成。

进程间通信

在分布式系统和并发编程中,进程间通信对于组件协调至关重要。Node.js 通过其 child 进程模块改进了这种通信。这使您能够创建 child 进程并促进进程间通信(IPC)。

这种 IPC 机制,特点是父进程和 child 进程之间交换数据,对于构建模块化和可扩展的应用程序至关重要。

有几种方法可以促进;一个突出的方法是通过 send() 方法和 message 事件进行消息传递。这种方法利用了 child 进程对象上可用的 send() 函数,向 child 进程发送消息,而 child 进程使用 message 事件侦听这些消息。

让我们用一个实际的例子来说明。我们将创建一个简单的应用程序,其中父进程分叉一个 child 进程,并使用 send() 方法和 message 事件与之通信。

父文件,即 parent.js,将如下所示:

const { fork } = require("child_process");const child = fork("./child.js");child.send({ greeting: "Hello from parent!" });child.on("message", (msg) => {console.log("Message from child:", msg);
});console.log("Waiting for response...");

child 进程文件,即 child.js

process.on("message", (msg) => {console.log("Message received from parent:", msg);process.send({ response: "Hello from child!" });
});

在这个例子中,父进程向 child 进程发送一条包含问候语的消息。child 进程侦听此消息,记录它,然后向父进程发送一条响应。父进程也侦听来自 child 进程的传入消息,并在收到时记录它们。

使用 send()message 事件进行 IPC 的这种模式在需要协调父进程和 child 进程之间的操作时特别有用,例如共享数据、触发操作或在 Node.js 应用程序中实现请求-响应模式。

img

管理子进程的最佳实践

以下是一些用于有效管理 child 进程的最佳实践:

  • 监控系统资源:最佳实践之一是定期监控系统的 CPU、内存和网络使用情况,以识别潜在的瓶颈或资源泄漏。使用诸如 top 和 ps 之类的工具可以为您提供资源消耗的实时洞察。

  • 为长时间运行的进程设置超时:使用 spawn() 中的 timeout 选项自动终止在指定持续时间后仍在运行的 child 进程。这对于避免长时间运行的活动占用资源非常有益。

  • 实施优雅的关闭程序:优雅关闭程序是指服务器已响应所有请求,并且没有剩余的数据处理工作。始终确保 child 进程可以优雅地关闭,释放资源并执行任何必要的清理工作。这可以通过侦听终止信号并相应地响应来实现。

  • 应用 kill 方法:使用适当的信号(例如,SIGTERM 用于优雅关闭,SIGKILL 作为最后手段)。验证进程是否已成功终止。在应用 kill 方法时处理潜在错误。

子进程与工作线程

虽然 child 进程对某些工作很有用,但 Node.js 还提供了另一种并行执行的方法。这种方法被称为工作线程,它与 child 进程完全不同。

worker threads 模块在 Node.js v10.50 中引入。与 child 进程相比,它提供了一种更有效的方式来处理多线程。下面是一个突出它们差异的表格

特性 工作线程 子进程
内存隔离 与父进程共享内存空间 每个子进程都有自己的内存堆(完全隔离)
通信 使用消息传递,开销较小(通过 SharedArrayBuffer) 通过 IPC 通道通信,开销较大
性能 更轻量级,频繁数据交换的开销较低 通信频繁时开销较高
用例 平行计算,需要频繁数据交换的任务 CPU 密集型任务,运行外部脚本,隔离任务
错误隔离 错误可能影响主线程 子进程中的错误不影响父进程
API 模块 worker_threads child_process
创建方法 new Worker() spawn(), fork(), exec(), execFile()
多线程 真正的多线程,共享内存 单独的进程,没有真正的多线程
开销 由于共享内存而较低 由于完全创建进程而较高
资源管理 在同一进程内管理 为每个进程分配单独的资源

何时使用子进程

当您需要完全隔离时,例如运行外部脚本或管理可能会使父进程崩溃的 CPU 密集型任务,可以使用 child 进程。

何时使用工作线程

另一方面,当任务从共享内存和频繁通信中受益时,可以使用 worker 线程,例如数据处理和并行计算。

结论

child 进程模块有助于执行并行任务,运行外部脚本和管理子进程。当您理解这些方法以及如何实现它们时,您将能够显著提高应用程序的性能和可扩展性。

本文由mdnice多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/795310.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HPC环境下数据下载:安全与效率的完美平衡!

在能源行业,高性能计算(HPC)应用广泛,比如地震数据处理、油气藏模拟和建模、地理空间分析、风模拟和地形映射等。随着数据量的爆发式增长,HPC环境下数据下载面临着严峻的安全挑战,如何在保障数据高效流转的同时,确保数据安全,成为能源企业必须面对的重要课题。 高性能…

【干货分享】Ftrans安全数据交换系统 搭建跨网数据传输通道

安全数据交换系统是一种专门设计用于在不同的网络、系统或组织之间安全地传输数据的软件或硬件解决方案。这种系统通常包含多种安全特性,以确保数据在传输过程中的保密性、完整性和可用性。 安全数据交换系统可以解决哪些问题? 安全数据交换系统主要解决以下问题: 数据泄露风…

采集DNP3设备数据 转 IEC61850项目案例

目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 准备工作 2 4 配置网关采集DNP3协议数据 2 5 用IEC61850协议转发数据 4 6 网关使用多个逻辑设备和逻辑节点的方法 6 7 案例总结 7 1 案例说明设置网关采集DNP3协议设备数据 把采集的数据转成IEC61850协议转发给其他系统。2 VFBOX网关…

强!70.3K star ! 推荐一款功能强大、开源、可视化的性能实时监控系统:Netdata

在当今复杂多变的IT环境中,系统性能的实时监控与分析对于确保业务连续性、系统稳定运行以及快速故障排查至关重要。随着云计算、大数据和微服务架构的普及,对监控系统的要求也日益增高。 今天给大家推荐一款性能监控工具为:Netdata。 它作为一款开源、实时、轻量级的系统性能…

windows 上 Tauri 开发环境配置

Tauri 是一个构建适用于所有主流桌面和移动平台的轻快二进制文件的框架。开发者们可以集成任何用于创建用户界面的可以被编译成 HTML、JavaScript 和 CSS 的前端框架,同时可以在必要时使用 Rust、Swift 和 Kotlin 等语言编写后端逻辑。 Tauri 使用 Microsoft C++ 构建工具进行…

南沙C++信奥老师解一本通题:1203:扩号匹配问题

​ 【题目描述】在某个字符串(长度不超过100)中有左括号、右括号和大小写字母;规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。写一个程序,找到无法匹配的左括号和右括号,输出原来字符串,并在下一行标出不能匹配的括号。不能匹…

Cisco Catalyst Center 2.3.7.6-VA 发布下载,新增功能概览

Cisco Catalyst Center 2.3.7.6-VA 发布下载,新增功能概览Cisco Catalyst Center 2.3.7.6-VA - 网络管理和自动化 Cisco Catalyst Center - Network Management and Automation 请访问原文链接:https://sysin.org/blog/cisco-catalyst-center/,查看最新版。原创作品,转载请…

macOS Sequoia 15 RC (24A335) Boot ISO 发布下载,正式版下周公布

macOS Sequoia 15 RC (24A335) Boot ISO 发布下载,正式版下周公布macOS Sequoia 15 RC (24A335) Boot ISO 原版可引导镜像下载 iPhone 镜像、Safari 浏览器重大更新、备受瞩目的游戏和 Apple Intelligence 等众多全新功能令 Mac 使用体验再升级 请访问原文链接:https://sysin…

网络流与图匹配

大风带来秋意一丝【学习笔记】网络流 (2024.4.23)时代的眼泪,近几年真的有考过吗,感觉不如二分图。Part1. 基本定义流函数 \(f\) 满足 1.斜对称性 2.流量守恒。 定义残量网络为容量函数 \(c=c-f\) 的网络,当 \(c=0\) 视作不存在。 定义增广路 \(P(V,E_f),\) 是残量网络上一条…

C#/.NET/.NET Core优秀项目和框架2024年8月简报

前言 公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项…

WPF 已知问题 监听 WMI 事件导致触摸失效

本文记录 WPF 的已知问题,在 .NET Core 版本的 WPF 应用里面,应用启动的过程监听 WMI 事件,将导致触摸模块 COM 接口获取失败,进而导致触摸失效此问题仅在 .NET Core 版本复现,在 .NET Framework 框架下能正常工作 复现步骤如下:安装 System.Management 库用于使用 WqlEv…

读软件设计的要素07读后总结与感想兼导读

读后总结与感想兼导读1. 基本信息 软件设计的要素 丹尼尔杰克逊著 浙江教育出版社,2024年5月出版 1.1. 读薄率 书籍总字数236千字,笔记总字数18853字。 读薄率18853236000≈8% 1.2. 读厚方向构建可扩展分布式系统方法与实践设计模式:可复用面向对象软件的基础程序员修炼之道:…