详解如何利用Nodejs构建多进程应用

目录
  • 前言
  • 进程的创建和使用
    • 多核利用率
    • 创建子进程
    • 进程间通信 IPC
  • 总结

前言

JavaScript 主线程运行在单个进程的单个线程上。这样做的好处是:

  • 程序状态是单一的,在没有多线程的情况下没有锁、线程同步问题,
  • 操作系统在调度时因为较少上下文的切换,可以很好地提高CPU的使用率。

但是单进程单线程并非完美的结构,一旦线程中某段代码发生异常阻塞,会阻塞代码执行而浪费资源。同时,如今CPU基本均是多核的,服务器往往还有多个CPU。

因此 Nodejs 不可避免的面临两个问题:如何充分利用多核CPU ****和 ****如何保证进程的健壮性和稳定性 。 另外,由于 Nodejs 中所有处理都在单线程上进行,影响事件驱动服务模型性能的点在于CPU的计算能力,而不受多进程或多线程模式中资源上限的影响,可伸缩性远比前两者高。如果解决掉多核CPU的利用问题,带来的性能上提升是可观的。

进程的创建和使用

多核利用率

面对单进程单线程对多核使用不足的问题,最简单的方法是启动多进程即可。理想状态下每个进程各自利用一个CPU,以此实现多核CPU的利用。Nodejs 提供了 child_process模块,并且也提供了child_process.fork() 方法帮助我们实现进程的复制。

你可以通过这个方法在本地启动多个 HTTP 服务,首先编写一段创建 http服务端 的代码:

const http = require("http");
const server = http.createServer((req, res) => {
  res.writeHead(200, {
    "Content-Type": "text/plain"
  });
  res.end("hello,world!");
});
// 随机监听1000-1999的任意一个端口
server.listen(Math.floor((1 + Math.random()) * 1000));

然后创建一个主进程 master.js 来启动和管理他们:

const fork = require("child_process").fork;
const cpus = require("os").cpus();
console.log(cpus.length);
for (let i = 0; i < cpus.length; i++) {
  fork('./server.js');
}

在 Linux 系统中,你可以通过 ps aux | grep worker.js 来直接查看,在 Windows 中,你能通过 netstat -a 然后查看 1000-1999 端口的进程。

这样的模式称为 Master-Worker模式,又称 主从模式。如图,进程分为两种:主进程和工作进程。这是典型的分布式架构中用于并行处理业务的模式,具备较好的可伸缩性和稳定性:

  • 主进程不负责具体的业务处理,而是负责调度或管理工作进程,它是趋向于稳定的。
  • 工作进程负责具体的业务处理,趋于不稳定。

fork() 能让我们复制进程使每个CPU内核都使用上,但是依然要切记 fork() 进程是昂贵的。因为Node通过事件驱动和异步IO的方式很大的缓解了并发问题,所以这里启动多个进程只是为了充分将CPU资源利用起来,而不是为了解决并发问题。

创建子进程

child_process模块 给予 Node 可以随意创建子进程(child_process)的能力。它提供了4个方法用于创建子进程:

  • spawn():启动一个子进程来执行命令。
  • exec():启动一个子进程来执行命令。与spawn()不同的是其接口不同,它有一个回调函数获知子进程的状况。
  • execFile():启动一个子进程来执行可执行文件。
  • fork():与 spawn() 类似,不同点在于它创建 Node 的子进程只需指定要执行的 JavaScript 文件模块即可。
const cp = require("child_process");
cp.spawn("node", ["./server.js"]);
cp.exec("node server.js", (err, stdout, stderr) => {
  // TODO
});
cp.execFile("server.js", (err, stdout, stderr) => {
  // TODO
});
cp.fork('./server.js');

execFile() 只能用于可以直接执行的文件。在 Linux 中,你可以在文件开头加入 #! /usr/bin/env node

进程间通信 IPC

Master-Worker模式 中,要实现主进程管理和调度工作进程的功能,需要主进程和工作进程之间的通信。子进程对象则由 send() 方法实现主进程向子进程发送数据,message事件 实现收听子进程发来的数据:

// parent.js
const cp = require("child_process");
const n = cp.fork("./child.js");
n.on('message', (msg) => {
  console.log('parent got msg: ', msg);
});
n.send({
  s: 'hello, world',
});
// child.js
process.on('message', (msg) => {
  console.log('child got msg', msg);
});
process.send({
  b: 'bar',
});

HTML5提出了 WebWorker API 来创建工作线程,而主进程和工作进程之间通过 onmessage()postMessage() 进行通信。具体参考MDN文档

通过 fork() 或者其他API,创建子进程之后,为了实现父子进程之间的通信,父进程与子进程之间将会创建IPC通道(Inter-Process Communication,即进程间通信) 。通过IPC通道,父子进程之间才能通过message和send()传递消息。

父进程在实际创建子进程之前,会创建IPC通道并监听它,然后才真正创建出子进程,并通过环境变量(NODE_CHANNEL_FD)告诉子进程这个IPC通道的文件描述符。子进程在启动的过程中,根据文件描述符去连接这个已存在的IPC通道,从而完成父子进程之间的连接。

只有启动的子进程是Node进程时,子进程才会根据环境变量去连接IPC通道。对于其他类型的子进程则无法实现进程间通信,除非其他进程也按约定去连接这个已经创建好的IPC通道

总结

今天我们讨论了 Nodejs 在多核利用方法的问题,然后介绍了创建子进程、进程间通信与IPC通道的内容。通过这些基础技术,用 child_process模块 在单机上搭建 Nodejs集群 是件相对容易的事情,让 Nodejs 能够在多核CPU的环境下充分利用计算资源。

以上就是详解如何利用Nodejs构建多进程应用的详细内容,更多关于Nodejs构建多进程应用的资料请关注我们其它相关文章!

(0)

相关推荐

  • Nodejs中解决cluster模块的多进程如何共享数据问题

    前述 nodejs在v0.6.x之后增加了一个模块cluster用于实现多进程,利用child_process模块来创建和管理进程,增加程序在多核CPU机器上的性能表现.本文将介绍利用cluster模块创建的多线程如何共享数据的问题. 进程间数据共享 首先举个简单的例子,代码如下: var cluster = require('cluster'); var data = 0;//这里定义数据不会被所有进程共享,各个进程有各自的内存区域 if (cluster.isMaster) { //主进程

  • 深入理解NodeJS 多进程和集群

    进程和线程 "进程" 是计算机系统进行资源分配和调度的基本单位,我们可以理解为计算机每开启一个任务就会创建至少一个进程来处理,有时会创建多个,如 Chrome 浏览器的选项卡,其目的是为了防止一个进程挂掉而应用停止工作,而 "线程" 是程序执行流的最小单元,NodeJS 默认是单进程.单线程的,我们将这个进程称为主进程,也可以通过 child_process 模块创建子进程实现多进程,我们称这些子进程为 "工作进程",并且归主进程管理,进程之间默

  • nodejs基础之多进程实例详解

    本文实例讲述了nodejs基础之多进程.分享给大家供大家参考,具体如下: Node.js 多进程 我们都知道 Node.js 是以单线程的模式运行的,但它使用的是事件驱动来处理并发,这样有助于我们在多核 cpu 的系统上创建多个子进程,从而提高性能. 每个子进程总是带有三个流对象:child.stdin, child.stdout 和child.stderr.他们可能会共享父进程的 stdio 流,或者也可以是独立的被导流的流对象. Node 提供了 child_process 模块来创建子进程

  • Nodejs搭建多进程Web服务器实现过程

    目录 前言 父子进程间通信 负载均衡 句柄传递 集群 子进程事件 自动重启 总结 前言 上节我们讲到,通过 fork() 或者其他API,创建子进程之后,可以通过 send() 和 process.on('message') 进行父子进程间的通信.这样就实现了主进程代理请求到工作进程,实现了 Nodejs集群: 父子进程间通信 负载均衡 通过代理,可以避免端口不能重复监听的问题,甚至可以在代理进程上做适当的负载均衡,使得每个子进程可以较为均衡地执行任务.下面我们构建了一个简单的 Web 服务器,

  • 详解如何利用Nodejs构建多进程应用

    目录 前言 进程的创建和使用 多核利用率 创建子进程 进程间通信 IPC 总结 前言 JavaScript 主线程运行在单个进程的单个线程上.这样做的好处是: 程序状态是单一的,在没有多线程的情况下没有锁.线程同步问题, 操作系统在调度时因为较少上下文的切换,可以很好地提高CPU的使用率. 但是单进程单线程并非完美的结构,一旦线程中某段代码发生异常阻塞,会阻塞代码执行而浪费资源.同时,如今CPU基本均是多核的,服务器往往还有多个CPU. 因此 Nodejs 不可避免的面临两个问题:如何充分利用多

  • 详解Python利用APScheduler框架实现定时任务

    目录 背景 样例代码 代码详解 执行结果 知识点补充 背景 最近在做一些python工具的时候,常常会碰到定时器问题,总觉着使用threading.timer或者schedule模块非常不优雅.所以这里给自己做个记录,也分享一个定时任务框架APScheduler.具体的架构原理就不细说了,用个例子说明一下怎么简易的使用. 样例代码 先上样例代码,如下: #!/user/bin/env python # coding=utf-8 """ @project : csdn @aut

  • 详解如何利用Python绘制科赫曲线

    目录 1. 递归 1.1 定义 1.2 数学归纳法 2. 递归的使用方法 2.1 阶乘 2.2 字符串反转 3. 科赫曲线的绘制 3.1 概要 3.2 绘制科赫曲线 3.3 科赫曲线的雪花效果 3.4 分形几何 1. 递归 1.1 定义 函数作为一种代码封装, 可以被其他程序调用,当然,也可以被函数内部代码调用.这种函数定义中调用函数自身的方式称为递归.就像一个人站在装满镜子的房间中,看到的影像就是递归的结果.递归在数学和计算机应用上非常强大,能够非常简洁地解决重要问题. 数学上有个经典的递归例

  • 详解如何利用Redis实现生成唯一ID

    目录 一.摘要 二.方案实践 2.1.引入 redis 组件 2.2.添加 redis 环境配置 2.3.编写服务验证逻辑,通过 aop 代理方式实现 2.4.在相关的业务接口上,增加SubmitLimit注解即可 三.小结 一.摘要 在上一篇文章中,我们详细的介绍了随着下单流量逐渐上升,为了降低数据库的访问压力,通过请求唯一ID+redis分布式锁来防止接口重复提交,流程图如下! 每次提交的时候,需要先调用后端服务获取请求唯一ID,然后才能提交. 对于这样的流程,不少的同学可能会感觉到非常鸡肋

  • 详解Swift 利用Opration和OprationQueue来下载网络图片

    详解Swift 利用Opration和OprationQueue来下载网络图片 1. 基于Opration封装的获取网络数据组件 import Foundation import UIKit public typealias OpreationClosure = ((_ data:Data? , _ error: Error?) -> Void) class LJOpreationManager: Operation { /** * 下载用的url */ public var imageUrl

  • 详解IOS 利用storyboard修改UITextField的placeholder文字颜色

    详解IOS 利用storyboard修改UITextField的placeholder文字颜色 最近有个需求需要修改UITextField的placeholder文字颜色,在网上找发现有用代码修改的,但是考虑到更加优雅的实现,所以尝试着在storyboard中直接实现,结果竟然真的成功了, 实现的位置如下: 具体步骤: 1.在User Defined Runtime Attributes中添加一个Key. 2.输入Key Path(这里我们输入_placeholderLabel.textColo

  • 详解DevEco Studio项目构建讲解、编写页面、布局介绍、页面跳转

    首先要知道鸿蒙的APP是怎么构成的?   HarmonyOS的应用软件包以APP Pack(Application Package)形式发布,它是由一个或多个HAP(HarmonyOS Ability Package)以及描述每个HAP属性的pack.info组成.HAP是Ability的部署包,HarmonyOS应用代码围绕Ability组件展开. 一个HAP是由代码.资源.第三方库及应用配置文件组成的模块包,可分为entry和feature两种模块类型,如下图所示. 一.项目目录 首先来看一

  • 详解Python利用configparser对配置文件进行读写操作

    简介 想写一个登录注册的demo,但是以前的demo数据都写在程序里面,每一关掉程序数据就没保存住.. 于是想着写到配置文件里好了 Python自身提供了一个Module - configparser,来进行对配置文件的读写 Configuration file parser. A configuration file consists of sections, lead by a "[section]" header, and followed by "name: valu

  • 详解如何利用tushare、pycharm和excel三者结合进行股票分析

    前言 当你逐渐了解tushare之后,你会发现我们要进行数据分析只靠tushare是不够的,接下来我将介绍如何利用第三方软件将tushare获取的数据进行分析. 一.使用工具 首先我们需要下载pycharm,下载网址:https://www.jetbrains.com/pycharm/download/,同时准备两个Excel文件,一个用了存放获取的数据,另一个进行数据的引用与分析. 二.使用步骤 1.首先在pycharm的setting中下载三个包:numpy.tushare.matplotl

  • 详解Java利用深度优先遍历解决迷宫问题

    目录 什么是深度优先 一个简单的例子 程序实现 什么是深度优先 什么是深度,即向下,深度优先,即向下优先,一口气走到底,走到底发现没路再往回走. 在算法实现上来讲,深度优先可以考虑是递归的代名词,深度优先搜索必然需要使用到递归的思路. 有的人可能会说了,我可以用栈来实现,以迭代的方式,那么问题来了,栈这种数据结构,同学们认为是否也囊括了递归呢?Java语言的方法区本身也是实现在一个栈空间上的. 一个简单的例子 我们以一个简单的迷宫为例,以1代表墙,0代表路径,我们构造一个具有出入口的迷宫. 1

随机推荐