关于 Nodejs cluster 的问题(用自带的 cluster)

关于 Nodejs cluster 的问题(用自带的 cluster)

var cluster = require(‘cluster’); var http = require(‘http’); var numCPUs = require(‘os’).cpus().length;

// 获取 CPU 的数量 var workers = {}; if (cluster.isMaster) { console.log(‘master…’); // 初始化 CPU 数量相同的工作进程 for (var i = 0; i < numCPUs; i++) { var worker = cluster.fork(); workers[worker.pid] = worker; }

// 主进程分支
cluster.on('death', function (worker) {
    // 当一个工作线程结束时,重新启动一个工作线程
    delete workers[worker.pid];
    worker = cluster.fork();
    workers[worker.pid] = worker;
});

} else { // 工作進进程分支,启动服务器 console.log(‘worker----’);

http.createServer(function(req, res){
    console.log('%s %s', req.method, req.url);
    var body = 'Hello World';
    res.writeHead(200, { 'Content-Length': body.length });
    res.end(body);
}).listen(9000);

}

// 当主进程终止时,关闭所有工作进程 process.on(‘SIGTERM’, function () { for (var pid in workers) { process.kill(pid); } process.exit(0); });

================================ 以上代码,按设计应该是输出:'Hello World’ 但并没有输出,只在启动的时候输出了: master… worker---- worker---- worker---- worker---- worker---- worker---- worker---- worker----

在页面上访问,没有任何反应,最后页面访问失败;

============================================================

而下面这个代码就可以正常访问(把createServer放在前面),输出的结果也对,但用AB压了一下,发现用 1个子进程跟8个子进程基本上没有任何区别!

( 求解啊,这是为啥呢,是上面的程序哪里写的不对? 还是下面的代码还是相当于只用了一个子进程来处理请求?);

cup: 8核; 并发: 1000次 总请求:2000次

var cluster = require(‘cluster’); var http = require(‘http’); var numCPUs = require(‘os’).cpus().length;

http.createServer(function(req, res){ console.log(’%s %s’, req.method, req.url); var body = ‘Hello World’; res.writeHead(200, { ‘Content-Length’: body.length }); res.end(body); }).listen(9000);

// 获取 CPU 的数量 var workers = {}; if (cluster.isMaster) { console.log(‘master…’); // 初始化 CPU 数量相同的工作进程 for (var i = 0; i < numCPUs; i++) { var worker = cluster.fork(); workers[worker.pid] = worker; }

// 主进程分支
cluster.on('death', function (worker) {
    // 当一个工作线程结束时,重新启动一个工作线程
    delete workers[worker.pid];
    worker = cluster.fork();
    workers[worker.pid] = worker;
});

} else { // 工作進进程分支,启动服务器 console.log(‘worker----’);

}

// 当主进程终止时,关闭所有工作进程 process.on(‘SIGTERM’, function () { for (var pid in workers) { process.kill(pid); } process.exit(0); });


6 回复

关于 Node.js Cluster 模块的问题

问题描述

在使用 Node.js 的 cluster 模块时,我们期望通过多进程的方式来提高应用的并发处理能力。然而,在实际操作中,无论是将 createServer 放在主进程中还是工作进程中,都发现多进程的效果并不明显。

示例代码分析

首先,我们来看一下原始代码:

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

// 获取 CPU 的数量
var workers = {};

if (cluster.isMaster) {
    console.log('master...');
    // 初始化 CPU 数量相同的工作进程
    for (var i = 0; i < numCPUs; i++) {
        var worker = cluster.fork();
        workers[worker.pid] = worker;
    }

    // 主进程分支
    cluster.on('death', function (worker) {
        // 当一个工作线程结束时,重新启动一个工作线程
        delete workers[worker.pid];
        worker = cluster.fork();
        workers[worker.pid] = worker;
    });
} else {
    // 工作进程分支,启动服务器
    console.log('worker----');

    http.createServer(function(req, res) {
        console.log('%s %s', req.method, req.url);
        var body = 'Hello World';
        res.writeHead(200, { 'Content-Length': body.length });
        res.end(body);
    }).listen(9000);
}

// 当主进程终止时,关闭所有工作进程
process.on('SIGTERM', function () {
    for (var pid in workers) {
        process.kill(pid);
    }
    process.exit(0);
});

问题所在

  1. createServer 放在主进程中:如果将 createServer 放在主进程中,那么所有的请求都会被主进程处理,而不会分发到各个工作进程中。这会导致多进程的优势无法体现出来。

  2. createServer 放在工作进程中:如果将 createServer 放在每个工作进程中,那么每个工作进程都会监听同一个端口,这会导致冲突。正确的做法是让主进程监听端口,并将连接分发给各个工作进程。

解决方案

以下是修正后的代码示例:

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    console.log(`Master ${process.pid} is running`);

    // Fork workers.
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', (worker, code, signal) => {
        console.log(`Worker ${worker.process.pid} died`);
    });

    // Listen on a port and distribute requests to workers
    const server = http.createServer((req, res) => {
        console.log(`Request received by master: ${req.url}`);
        res.writeHead(200);
        res.end('Hello World');
    });

    server.listen(9000, () => {
        console.log('Server listening on port 9000');
    });

} else {
    // Workers can share any TCP connection
    // In this case it is an HTTP server
    console.log(`Worker ${process.pid} started`);

    http.createServer((req, res) => {
        console.log(`Request received by worker ${process.pid}: ${req.url}`);
        res.writeHead(200);
        res.end('Hello World');
    }).listen(9000);
}

关键点解释

  1. 主进程监听端口:主进程负责创建 HTTP 服务器并监听端口。这样可以确保请求能够被正确地分发到各个工作进程中。

  2. 工作进程处理请求:每个工作进程创建自己的 HTTP 服务器实例,但它们不直接监听端口。而是由主进程将请求转发到这些工作进程。

  3. 请求分发:Node.js 会自动将请求分发到不同的工作进程,从而实现负载均衡。

通过这种方式,你可以充分利用多核 CPU 的优势,提高应用的并发处理能力。


原因是:if (cluster.isMaster) 它一直为真,所以就没有运行到 http.createServer, 如果不是把http.createServer 放在第一行的话;

可是这是为什么呢? 难道网上的示例都是没有经过自己手动跑过的?

我找到问题了!@!~

是我自己的问题, sorry~

在你的代码中,问题在于HTTP服务器的创建位置。在第一个代码片段中,HTTP服务器是在每个工作进程中创建的,但在实际操作中,只有最后一个工作进程中的服务器会被绑定到端口并接受请求。因此,尽管你创建了多个工作进程,但实际上只有其中一个进程在处理请求。

下面是修改后的代码,确保HTTP服务器只在一个工作进程中创建,并通过负载均衡分配请求到不同的工作进程:

修改后的代码

var cluster = require('cluster');
var http = require('http');
var os = require('os');

var numCPUs = os.cpus().length;

if (cluster.isMaster) {
    console.log('master...');
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }
    
    cluster.on('exit', function(worker, code, signal) {
        console.log(`Worker ${worker.process.pid} died`);
        cluster.fork();
    });
} else {
    console.log('worker----');

    http.createServer(function(req, res) {
        console.log('%s %s', req.method, req.url);
        var body = 'Hello World';
        res.writeHead(200, { 'Content-Length': Buffer.byteLength(body) });
        res.end(body);
    }).listen(9000);
}

process.on('SIGTERM', function() {
    for (var pid in cluster.workers) {
        process.kill(pid);
    }
    process.exit(0);
});

解释

  1. HTTP服务器的创建:确保HTTP服务器只在工作进程中创建,而不是在主进程中。
  2. 负载均衡:Node.js的cluster模块会自动进行负载均衡,将请求分发到不同的工作进程。
  3. 错误处理:当一个工作进程退出时,会重新启动一个新的工作进程。

测试

你可以使用Apache Bench (ab) 或其他工具进行压力测试,验证多个工作进程是否能有效分担负载。

通过这种方式,你可以确保所有的CPU核心都被充分利用,提高应用程序的性能和吞吐量。

回到顶部