Nodejs如何让node.js充分利用多核服务器的性能

Nodejs如何让node.js充分利用多核服务器的性能

之前和PHP同事讨论node.js的性能问题,看了一些node.js的相关文档,node.js除了拥有非阻塞I/O,快速开发等诸多优点外,其缺点也很明显:

1、单进程,只支持单核CPU,不能充分的利用多核CPU服务器。

2、单进程,一旦这个进程崩掉,那么整个web服务就崩掉了。(当然这个可以通过代码的健壮性来弥补)

开发环境:

vMware Red Hat 虚拟机

CPU: 两个

内存:1GB

<span style=“color: #ff0000”>以下有说的不对的地方还请多多指教:



近期我借助node.js开发了一套网络聊天室的应用,简单实现了大厅聊天,私聊,群聊。用户信息、群组成员信息和聊天内容等借用redis进行存储。

具体聊天室的功能请参阅我的博客:http://snoopyxdy.blog.163.com/blog/static/60117440201172510426330/

现在想要1个CPU绑定一个node.js聊天室进程,需要借助这个命令:<span style=“color: #ff0000”>taskset,(具体请参阅相关文档)

在linux下运行:<span style=“color: #3366ff”><span style=“color: #3366ff”>
node server.js

node server0.js


我设定server.js监听端口为8888,server0.js监听端口为8889.

OK,现在2个node.js聊天室程序启动起来了。<span style=“color: #ff0000”>但是要注意,这2个聊天室不能相互聊天,但是用户表是一个,就当他是两个不同的房间把。聊天室代码是一套,只是拷贝了一份server.js,在里面监听了不同的端口。

然后我们要对这两个node.js的进程绑定不同的CPU:(目前我的虚拟机就2个CPU)

ps -ef | grep node  (用来查看node的进程号)

root 19277 2517 15 10:12 pts/1 00:00:20 node /usr/local/node/src/chating_express/server.js
root 19283 2566 18 10:13 pts/2 00:00:20 node /usr/local/node/src/chating_express/server0.js
root 19291 2384 0 10:15 pts/0 00:00:00 grep node

找到了上述2个PID后,就利用taskset来进行CPU绑定,命令如下:(这里只写其中一个)

taskset -pc 0 19277  (其中0代表CPU0,以此类推)

pid 19277’s current affinity list: 0,1
pid 19277’s new affinity list: 0

出现上述信息代表绑定成功,同样方法绑定另外一个。

绑定结束后我们怎么知道他们运行在不同的CPU上呢?找另外一台虚拟机,装好webbench,分别对两个node.js聊天室端口进行压力测试,然后用top命令查看各个CPU的使用情况,(小窍门,输入TOP命令,然后按数字“1”,在压力测试时不停的按“ENTER”),结果表明在压力测试server.js端口时CPU0使用率长期保持在60%以上而CPU1则使用率长期在20%以下,而测试server0.js端口时,CPU1长期保持在60%,而CPU0则保持在20%以下,结果表明绑定成功!(之前曾经乌龙了一次,用自己的虚拟机webbench压力测试,结果发现2个CPU使用率都很高,一直检查不出问题,后来发现webbench也要使用CPU的,泪奔!!!)

然后安装nginx,对nginx进行配置,只需在配置文件中加入:(具体nginx的安装和设置请参阅相关文档)

upstream node_server_pool {

server 10.1.1.202:8888 max_fails=1;
server 10.1.1.202:8889 max_fails=1;
}
server
{
listen 80;
server_name 10.1.1.202;
location /
{
proxy_pass http://node_server_pool;
proxy_set_header Host 10.1.1.202:80;
proxy_set_header X-Forwarded-For $remote_addr;
}

然后软重启nginx服务,访问10.1.1.202这个地址80端口,就可以让nginx做反向代理了和负载均衡了。

注:上述的这些配置需要node.js服务端代码良好的支持,session的存储建议使用redis,这样可以共享session否则需要用到ip_hash了(具体nginx优化配置请参阅相关文档),在多核CPU上跑多个node.js进程他们之间的内存是不共享的,这点需要牢记啊。

由于之前曾经压力测试过node.js的空框架,发现其内存消耗并不是很高,所以理论上来说,如果4核cpu且内存8G以上,开4个node.js进程跑在一个服务器上,用nginx做负载均衡,其性能和稳定性都应该比单个node.js提升很大。


16 回复

我想问下 如果不用taskset,测试的效果是怎样的呢。


Nodejs如何让Node.js充分利用多核服务器的性能

在讨论Node.js性能问题时,虽然Node.js以其非阻塞I/O和快速开发著称,但也存在一些明显的局限性:

  1. 单进程,只支持单核CPU:这意味着Node.js默认只能利用一个核心的计算能力。
  2. 单进程,一旦崩溃会导致整个Web服务崩溃:虽然可以通过提高代码的健壮性来缓解这个问题。

开发环境

  • VMware Red Hat 虚拟机
  • CPU:两个
  • 内存:1GB

实现思路

为了充分利用多核服务器的性能,我们可以采取以下几种策略:

  1. 多进程部署:通过创建多个Node.js进程,每个进程绑定到不同的CPU核心,从而实现负载均衡。
  2. 使用Nginx作为反向代理和负载均衡器:将请求分发到不同的Node.js实例,以平衡负载。

示例代码

假设我们有两个Node.js实例,分别监听不同的端口:

// server.js
const http = require('http');

const hostname = '127.0.0.1';
const port = 8888;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello from Server 1\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
// server0.js
const http = require('http');

const hostname = '127.0.0.1';
const port = 8889;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello from Server 2\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

绑定CPU核心

在Linux系统中,可以使用taskset命令将每个Node.js进程绑定到特定的核心。例如:

# 查找Node.js进程ID
ps -ef | grep node

# 绑定进程到CPU核心
taskset -pc 0 <PID_1>
taskset -pc 1 <PID_2>

配置Nginx作为反向代理

编辑Nginx配置文件,添加以下内容:

upstream node_server_pool {
  server 127.0.0.1:8888;
  server 127.0.0.1:8889;
}

server {
  listen 80;
  server_name localhost;

  location / {
    proxy_pass http://node_server_pool;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

测试与验证

使用webbench工具进行压力测试,并通过top命令监控CPU使用情况。确保两个Node.js实例分布在不同的CPU核心上。

总结

通过多进程部署和使用Nginx作为反向代理,我们可以有效利用多核服务器的性能,提高系统的稳定性和响应速度。同时,确保数据共享(如Session)的一致性也非常重要,可以考虑使用Redis等外部存储服务。

感谢jackson的关注,今天忙活了一天,终于把压力测试结果写出来了,已经提交审批了。感谢继续关注! <br/>大体结果性能提升在15%-20%左右,当然稳定更有保障了。

[@snoopy](/user/snoopy) <br/>都关心到进程和cpu了,老卵

聊天室的代码可以开源吗?

最后补充一下:大家比较关心的开多进程和单开性能对比,由于之后提交的压力测试结果文章一直没有被审核通过,我这里说一下: <br/>裸框架:单进程node.js和多进程node.js几乎差不多,多进程大约提升5%-10% <br/>业务处理压力:我是模拟18万次的循环浮点计算,多进程比单进程性能提升大约100%,用户响应速度提升200%,极限负载也要提升50%。 <br/>具体压测结果见:http://snoopyxdy.blog.163.com/blog/static/6011744020117315192204/

[@hymnfish](/user/hymnfish), <br/>聊天室的代码是学习用的,写的真的很一般,等我整理下,下周放到github上去把。

等待博主把这个聊天室的github地址中。。。

[@hymnfish](/user/hymnfish) <br/>[@xiongjiabin](/user/xiongjiabin) <br/>最近空了些,聊天室代码和演示都在: <br/>http://spout.cnodejs.net/

感谢[@alsotang](/user/alsotang) ,cnode 开启 html,兼容老帖

[@fengmk2](/user/fengmk2) 应该的。。应该的。。

Node.js进程间的内存不能共享是个巨大的遗憾。

这个是nginx做,不用 cluster ?

我擦,竟是四年前的帖子

好文。

为了充分利用多核服务器的性能,Node.js 可以通过集群模块(cluster)来创建子进程,每个子进程可以运行在不同的 CPU 上,从而实现负载均衡和提高性能。以下是一个简单的示例代码来展示如何使用 Node.js 的 cluster 模块。

示例代码

const cluster = require('cluster');
const os = require('os');

if (cluster.isMaster) {
  console.log(`主进程 ${process.pid} 正在运行`);

  // 计算可用的 CPU 核心数量
  const numCPUs = os.cpus().length;

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

  cluster.on('exit', (worker, code, signal) => {
    console.log(`工作进程 ${worker.process.pid} 已退出`);
  });
} else {
  // 这里是每个子进程将要运行的代码
  console.log(`工作进程 ${process.pid} 正在运行`);

  // 示例 HTTP 服务器
  const http = require('http');
  const PORT = process.env.PORT || 8000;

  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Hello World\n');
  }).listen(PORT);

  console.log(`HTTP 服务器正在监听端口 ${PORT}`);
}

解释

  1. 主进程 (cluster.isMaster):

    • 主进程负责生成子进程。
    • 使用 os.cpus().length 来获取当前系统的 CPU 核心数量。
    • 通过 cluster.fork() 方法创建指定数量的子进程。
  2. 子进程 (!cluster.isMaster):

    • 子进程运行实际的业务逻辑。
    • 在这个例子中,子进程运行一个简单的 HTTP 服务器。

如何运行

  1. 将上述代码保存到一个文件,例如 cluster-server.js
  2. 使用命令行运行该文件:
    node cluster-server.js
    
  3. 你可以使用 ps 命令查看当前运行的进程,并确认它们是否在不同的 CPU 核心上运行。

绑定 CPU 核心

如果你希望进一步绑定特定的 CPU 核心,可以使用 taskset 命令。例如,将第一个子进程绑定到 CPU 0,第二个子进程绑定到 CPU 1:

taskset -c 0,1 node cluster-server.js

这样,你可以确保每个子进程都在不同的 CPU 核心上运行,从而更好地利用多核服务器的性能。

回到顶部