Nodejs cluster测试

Nodejs cluster测试

在双核上开两个worker,阻塞一个worker后,其他worker也被阻塞了。开10个worker也一样,是我cpu核心太少吗?还是本来就这样。

连续发送两个请求,只有一个核是100%,并且从输出看只有一个节点在工作:

A worker with #1 is now connected to 0.0.0.0:8000
A worker with #2 is now connected to 0.0.0.0:8000
Worker #1 has a request
Worker #1 make a response
Time: 5000ms
Worker #1 has a request
Worker #1 make a response
Time: 5001ms

脚本文件: var cluster = require(‘cluster’); var http = require(‘http’); var numCPUs = require(‘os’).cpus().length;

if (cluster.isMaster) {
  require('os').cpus().forEach(function(){
    cluster.fork();
  });
  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });
  cluster.on('listening', function(worker, address) {  
    console.log("A worker with #"+worker.id+" is now connected to " +
     address.address +
    ":" + address.port);  
  }); 
} else {
  http.createServer(function(req, res) {
    console.log('Worker #' + cluster.worker.id + ' has a request');
    res.writeHead(200);
    console.time('Time');
    function sleep(milliSeconds) {
      var startTime = new Date().getTime();
      while (new Date().getTime() < startTime + milliSeconds);
    }
    sleep(5000);
    res.end("hello world\n");
    console.log('Worker #' + cluster.worker.id + ' make a response');
    console.timeEnd('Time');
  }).listen(8000);
}

9 回复

Node.js Cluster 测试

在使用 Node.js 的 cluster 模块时,你可能会遇到一些困惑,特别是在处理多核 CPU 和并发请求时。你提到的情况可能是由于某些原因导致多个 Worker 实际上没有并行处理请求。让我们通过分析你的代码和问题来理解可能的原因。

问题描述

你在双核或四核 CPU 上启动了多个 Worker(例如两个或十个),但只看到一个 Worker 在处理请求。你怀疑这是否是因为 CPU 核心数量不够,或者是因为这是 Node.js 的正常行为。

示例代码分析

首先,我们来看一下你的代码:

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

if (cluster.isMaster) {
  // 创建与 CPU 核心数量相同的 Worker
  require('os').cpus().forEach(function() {
    cluster.fork();
  });

  // 监听 Worker 启动事件
  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });

  // 监听 Worker 连接事件
  cluster.on('listening', function(worker, address) {
    console.log("A worker with #" + worker.id + " is now connected to " +
      address.address + ":" + address.port);
  });
} else {
  // Worker 端的 HTTP 服务器
  http.createServer(function(req, res) {
    console.log('Worker #' + cluster.worker.id + ' has a request');
    res.writeHead(200);

    console.time('Time');
    function sleep(milliSeconds) {
      var startTime = new Date().getTime();
      while (new Date().getTime() < startTime + milliSeconds);
    }
    sleep(5000); // 模拟长时间运行的任务
    res.end("hello world\n");
    console.log('Worker #' + cluster.worker.id + ' make a response');
    console.timeEnd('Time');
  }).listen(8000);
}

问题分析

  1. 单线程模型:尽管你创建了多个 Worker,每个 Worker 实际上仍然是单线程的。这意味着每个 Worker 只能处理一个请求,直到当前请求处理完毕。因此,即使你有多个 Worker,如果其中一个 Worker 正在处理一个长时间运行的任务(如 sleep(5000)),其他请求将被阻塞。

  2. CPU 核心限制:虽然你创建了多个 Worker,但如果你的长时间运行任务占用了 CPU 资源,其他 Worker 可能无法有效利用空闲的 CPU 核心。

解决方案

为了实现真正的并发处理,可以考虑以下几点:

  1. 异步处理:避免使用阻塞操作(如 sleep 函数)。改用异步方法(如 setTimeout 或第三方库如 async)来处理长时间运行的任务。

    setTimeout(() => {
      res.end("hello world\n");
      console.log('Worker #' + cluster.worker.id + ' make a response');
      console.timeEnd('Time');
    }, 5000);
    
  2. 负载均衡:确保每个 Worker 都能够有效地处理请求。你可以通过监控和调整 Worker 数量来优化性能。

  3. 非阻塞 I/O:Node.js 本身就是基于事件驱动和非阻塞 I/O 的,所以尽量避免阻塞操作。

通过这些改进,你应该能够更好地利用多核 CPU 并实现真正的并发处理。


关注

我的四核,两个核用到了。

C:\dev\test\node-module\cluster>node test.js
cpu#:4
cpu#:4
A worker with #1 is now connected to 0.0.0.0:8000
cpu#:4
A worker with #4 is now connected to 0.0.0.0:8000
cpu#:4
A worker with #3 is now connected to 0.0.0.0:8000
cpu#:4
A worker with #2 is now connected to 0.0.0.0:8000
Worker #2 has a request
Worker #2 make a response
Time: 5001ms
Worker #2 has a request
Worker #2 make a response
Time: 5000ms
Worker #2 has a request
Worker #2 make a response
Time: 5000ms
Worker #2 has a request
Worker #3 has a request
Worker #2 make a response
Time: 5000ms
Worker #2 has a request
Worker #3 make a response
Time: 5001ms
Worker #3 has a request
Worker #2 make a response
Time: 5000ms
Worker #2 has a request
Worker #3 make a response
Time: 5000ms
Worker #3 has a request
Worker #2 make a response
Time: 5000ms

请参考 http://www.cnblogs.com/tingshuo/archive/2013/01/17/2864280.html

cluster是在并发高的情况下才会充分利用多核

其实,是我测试方式不对

cluster 的负载均衡做的不好

cluster在window下是不能多核负载均衡,在linux下才会

cluster在node v0.11之前的版本,负载都十分不均衡,目前看来v0.11的 cluster 已经修复了这个问题,期待0.12的稳定版早日release吧…

在Node.js中使用cluster模块可以实现多进程并行处理网络请求,从而充分利用多核CPU资源。但是,需要注意的是,如果你的阻塞操作(例如长时间的计算或sleep)在一个worker进程中执行,其他worker不会自动接管这个请求。

在你的例子中,因为所有的HTTP请求都在一个worker中处理,并且这个worker被阻塞了,所以其他worker没有机会处理新的请求。这并不是因为CPU核心少,而是因为你的应用逻辑导致了这种现象。

为了解决这个问题,你可以尝试将请求分发到不同的worker进程来避免单个worker被阻塞的情况。你可以手动将请求分配给不同的worker,或者使用一些负载均衡策略,比如轮询或随机分配。

以下是一个简单的示例代码,通过轮询方式将请求分配给不同的worker:

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

if (cluster.isMaster) {
  var workerIndex = 0;
  require('os').cpus().forEach(function() {
    cluster.fork();
  });

  const workers = [];
  cluster.on('fork', (worker) => {
    workers.push(worker);
  });

  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });

  cluster.on('listening', function(worker, address) {
    console.log("A worker with #" + worker.id + " is now connected to " + address.address + ":" + address.port);
  });

  http.createServer((req, res) => {
    var worker = workers[workerIndex];
    workerIndex = (workerIndex + 1) % workers.length;
    worker.send('dispatch', { req, res });
  }).listen(8000);
} else {
  process.on('message', (msg, handle) => {
    if (msg === 'dispatch') {
      http.ServerResponse.prototype.write = function(chunk, encoding) {
        process.send({ type: 'write', data: chunk.toString(encoding) });
      };
      http.ServerResponse.prototype.end = function(chunk, encoding) {
        process.send({ type: 'end', data: chunk ? chunk.toString(encoding) : null });
        this.emit('finish');
      };
      req.on('data', (chunk) => {
        process.send({ type: 'data', data: chunk.toString() });
      });
      req.on('end', () => {
        process.send({ type: 'end' });
      });
      req.resume();
    }
  });
}

这个示例展示了如何将请求分发到不同的worker,以实现负载均衡。请注意,这段代码只是一个基础示例,实际生产环境中可能需要更复杂的错误处理和优化。

回到顶部