深入剖析Node.js cluster模块

cluster模块概览

node实例是单线程作业的。在服务端编程中,通常会创建多个node实例来处理客户端的请求,以此提升系统的吞吐率。对这样多个node实例,我们称之为cluster(集群)。

借助node的cluster模块,开发者可以在几乎不修改原有项目代码的前提下,获得集群服务带来的好处。

集群有以下两种常见的实现方案,而node自带的cluster模块,采用了方案二。

方案一:多个node实例+多个端口

集群内的node实例,各自监听不同的端口,再由反向代理实现请求到多个端口的分发。

  1. 优点:实现简单,各实例相对独立,这对服务稳定性有好处。
  2. 缺点:增加端口占用,进程之间通信比较麻烦。

方案二:主进程向子进程转发请求

集群内,创建一个主进程(master),以及若干个子进程(worker)。由master监听客户端连接请求,并根据特定的策略,转发给worker。

  1. 优点:通常只占用一个端口,通信相对简单,转发策略更灵活。
  2. 缺点:实现相对复杂,对主进程的稳定性要求较高。

入门实例

在cluster模块中,主进程称为master,子进程称为worker。

例子如下,创建与CPU数目相同的服务端实例,来处理客户端请求。注意,它们监听的都是同样的端口。

// server.js
var cluster = require('cluster');
var cpuNums = require('os').cpus().length;
var http = require('http');

if(cluster.isMaster){
 for(var i = 0; i < cpuNums; i++){
  cluster.fork();
 }
}else{
 http.createServer(function(req, res){
  res.end(`response from worker ${process.pid}`);
 }).listen(3000);

 console.log(`Worker ${process.pid} started`);
}

创建批处理脚本:./req.sh。

#!/bin/bash

# req.sh
for((i=1;i<=4;i++)); do
 curl http://127.0.0.1:3000
 echo ""
done

输出如下。可以看到,响应来自不同的进程。

response from worker 23735
response from worker 23731
response from worker 23729
response from worker 23730

cluster模块实现原理

了解cluster模块,主要搞清楚3个问题:

  1. master、worker如何通信?
  2. 多个server实例,如何实现端口共享?
  3. 多个server实例,来自客户端的请求如何分发到多个worker?

下面会结合示意图进行介绍,源码级别的介绍,可以参考 笔者的github

问题1:master、worker如何通信

这个问题比较简单。master进程通过 cluster.fork() 来创建 worker进程。cluster.fork() 内部 是通过 child_process.fork() 来创建子进程。

也就是说:

  1. master进程、worker进程是父、子进程的关系。
  2. master进程、woker进程可以通过IPC通道进行通信。(重要)

问题2:如何实现端口共享

在前面的例子中,多个woker中创建的server监听了同个端口3000。通常来说,多个进程监听同个端口,系统会报错。

为什么我们的例子没问题呢?

秘密在于,net模块中,对 listen() 方法进行了特殊处理。根据当前进程是master进程,还是worker进程:

  1. master进程:在该端口上正常监听请求。(没做特殊处理)
  2. worker进程:创建server实例。然后通过IPC通道,向master进程发送消息,让master进程也创建 server 实例,并在该端口上监听请求。当请求进来时,master进程将请求转发给worker进程的server实例。

归纳起来,就是:master进程监听特定端口,并将客户请求转发给worker进程。

如下图所示:

问题3:如何将请求分发到多个worker

每当worker进程创建server实例来监听请求,都会通过IPC通道,在master上进行注册。当客户端请求到达,master会负责将请求转发给对应的worker。

具体转发给哪个worker?这是由转发策略决定的。可以通过环境变量NODE_CLUSTER_SCHED_POLICY设置,也可以在cluster.setupMaster(options)时传入。

默认的转发策略是轮询(SCHED_RR)。

当有客户请求到达,master会轮询一遍worker列表,找到第一个空闲的worker,然后将该请求转发给该worker。

master、worker内部通信小技巧

在开发过程中,我们会通过 process.on('message', fn) 来实现进程间通信。

前面提到,master进程、worker进程在server实例的创建过程中,也是通过IPC通道进行通信的。那会不会对我们的开发造成干扰呢?比如,收到一堆其实并不需要关心的消息?

答案肯定是不会?那么是怎么做到的呢?

当发送的消息包含cmd字段,且改字段以NODE_作为前缀,则该消息会被视为内部保留的消息,不会通过message事件抛出,但可以通过监听'internalMessage'捕获。

以worker进程通知master进程创建server实例为例子。worker伪代码如下:

// woker进程
const message = {
 cmd: 'NODE_CLUSTER',
 act: 'queryServer'
};
process.send(message);

master伪代码如下:

worker.process.on('internalMessage', fn);

相关链接

官方文档:https://nodejs.org/api/cluster.html

Node学习笔记:https://github.com/chyingp/nodejs-learning-guide

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 使用cluster 将自己的Node服务器扩展为多线程服务器

    用nodejs的朋友都有了解,node是单线程的,也就是说跑在8核CPU上,只能使用一个核的算力. 单线程一直是node的一个诟病,但随着0.6版本中引入cluster之后,这个情况则得到了改变,开发人员可以依靠cluster很轻松的将自己的Node服务器扩展为多线程服务器了. 什么是Cluster cluster是node提供的一个多线程库,用户可以使用它来创建多个线程,线程之间共享一个监听端口,当有外部请求这个端口时,cluster会将请求转发到随机线程里.因为每个node线程都会占用几十兆

  • Node学习记录之cluster模块

    在如今机器的CPU都是多核的背景下,Node的单线程设计已经没法更充分的"压榨"机器性能了.所以从v0.8开始,Node新增了一个内置模块--"cluster",故名思议,它可以通过一个父进程管理一坨子进程的方式来实现集群的功能. var cluster = require('cluster'); var http = require('http'); var numCPUs = require('os').cpus().length; // 获取CPU的个数 if

  • Node.js中多进程模块Cluster的介绍与使用

    前言 我们都知道nodejs最大的特点就是单进程.无阻塞运行,并且是异步事件驱动的.Nodejs的这些特性能够很好的解决一些问题,例如在服务器开发中,并发的请求处理是个大问题,阻塞式的函数会导致资源浪费和时间延迟.通过事件注册.异步函数,开发人员可以提高资源的利用率,性能也会改善.既然Node.js采用单进程.单线程模式,那么在如今多核硬件流行的环境中,单核性能出色的Nodejs如何利用多核CPU呢?创始人Ryan Dahl建议,运行多个Nodejs进程,利用某些通信机制来协调各项任务.目前,已

  • node.js使用cluster实现多进程

    首先郑重声明: nodeJS 是一门单线程!异步!非阻塞语言! nodeJS 是一门单线程!异步!非阻塞语言! nodeJS 是一门单线程!异步!非阻塞语言! 重要的事情说3遍. 因为nodeJS天生自带buff, 所以从一出生就受到 万千 粉丝的追捧(俺,也是它的死忠). 但是,傻逼php 竟然嘲笑 我大NodeJS 的性能. 说不稳定,不可靠,只能利用单核CPU. 辣鸡 nodeJS. 艹!艹!艹! 搞mo shi~ 但,大哥就是大哥,nodeJS在v0.8 的时候就已经加入了cluster

  • node.js中cluster的使用教程

    本文主要给大家介绍了关于node.js中cluster使用的相关教程,分享出来供大家参考学习,下面来看看详细的介绍: 一.使用NODE中cluster利用多核CPU var cluster = require('cluster'); var http = require('http'); var numCPUs = require('os').cpus().length; if (cluster.isMaster) { // 创建工作进程 for (var i = 0; i < numCPUs;

  • Node.js中的cluster模块深入解读

    预备知识 在如今机器的CPU都是多核的背景下,Node的单线程设计已经没法更充分的"压榨"机器性能了.所以从v0.8开始,Node新增了一个内置模块--"cluster",故名思议,它可以通过一个父进程管理一坨子进程的方式来实现集群的功能. 学习cluster之前,需要了解process相关的知识,如果不了解的话建议先阅读process模块.child_process模块. cluster借助child_process模块的fork()方法来创建子进程,通过fork

  • 详解通过源码解析Node.js中cluster模块的主要功能实现

    众所周知,Node.js中的JavaScript代码执行在单线程中,非常脆弱,一旦出现了未捕获的异常,那么整个应用就会崩溃.这在许多场景下,尤其是web应用中,是无法忍受的.通常的解决方案,便是使用Node.js中自带的cluster模块,以master-worker模式启动多个应用实例.然而大家在享受cluster模块带来的福祉的同时,不少人也开始好奇: 为什么我的应用代码中明明有app.listen(port);,但cluter模块在多次fork这份代码时,却没有报端口已被占用? Maste

  • 浅谈node中的cluster集群

    结论 虽然平常通过设置为CPU进程数的工作进程,但是可以超过这个数,并且并不是主进程先创建 if (cluster.isMaster) { // 循环 fork 任务 CPU i5-7300HQ 四核四进程 for (let i = 0; i < 6; i++) { cluster.fork() } console.log(chalk.green(`主进程运行在${process.pid}`)) } else { app.listen(1314) // export app 一个 Koa 服务器

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

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

  • node 利用进程通信实现Cluster共享内存

    Node.js的标准API没有提供进程共享内存,然而通过IPC接口的send方法和对message事件的监听,就可以实现一个多进程之间的协同机制,通过通信来操作共享内存. ##IPC的基本用法: // worker进程 发送消息 process.send('读取共享内存'); // master进程 接收消息 -> 处理 -> 发送回信 cluster.on('online', function (worker) { // 有worker进程建立,即开始监听message事件 worker.o

随机推荐