【译】 Node.js v0.12的新特性 -- Cluster模式采用Round-Robin负载均衡

原文:https://strongloop.com/strongblog/whats-new-in-node-js-v0-12-cluster-round-robin-load-balancing

Node.js v0.12的新特性 -- Cluster采用轮询调度算法来进行负载均衡

November 19, 2013 by Ben Noordhuis

欢迎来到由Node的核心提交者 Ben Noordhuis 和 Bert Belder撰写的系列博文的第一篇。本系列可能由7-8篇构成,主要涵盖了Node.js v0.12的新特性。本文主要是关于新的轮询调度集群算法。

回顾Node内置的cluster模式

忆往昔,Node令人扼腕之限制即其内在之单线程模式。不管你的机器有多少核心,Node只会利用一个(同时警告用户,某些操作会利用一个线程池。对于大多数程序来说,相对于总的CPU时间,这只是九牛一毛,因此这样做并不会对利用处理器资源起到真正的帮助)。

这也是Node.js v0.8引入内置的cluster模块的原因。cluster模块让你可以启动一个作为监管者的主进程,以及一个或多个做实际工作的工作进程。

其中的一个目的是使得你更容易创建“甩手掌柜”式的多进程服务器。完美情况下,你应该可以用一个现有的单进程程序制造出足够多的工作进程而不需要修改任何代码。

当然,事情没那么容易。但是cluster模块使得程序有很少甚至没有共享状态,或者可以将共享状态保存在外部资源中,比如数据库或者web service。将程序转换成集群模式基本上只需要几行代码:

var cluster = require(‘cluster‘);
var os = require(‘os‘);

if (cluster.isMaster)
  // Spawn as many workers as there are CPUs in the system.
  for (var i = 0, n = os.cpus().length; i < n; i += 1)
    cluster.fork();
else
  // Start the application.
  app();

程序也不需要知道它是在集群模式下运行。
加入你的app()是下面这样:

var http = require(‘http‘);

function app() {
  var server = http.createServer(function(req, res) {
    res.end(‘OK‘);
  });
  server.listen(8080, ‘www.example.com‘);
}

cluster模块的魔力保证了工作进程可以绑定被请求的地址和端口,即使其他工作进程已经监听了。另外,它保证了外部连接被平均分配到监听的工作进程 -- 至少是理论上。

在Node.js v0.8 和 v0.10中,分配外部连接的算法是很直接的。当工作进程调用http.Server#listen()或者net.Server#listen()时,Node.js 发送消息告诉主进程去创建并绑定一个服务端socket并共享给该工作进程。如果已经有了一个被绑定的socket,主进程就跳过创建并绑定的阶段,直接共享已存在的socket给该工作进程。

这意味着所有的工作进程都监听同一个socket。当新的连接进入时,操作系统唤醒某个工作进程。该进程于是接受该连接然后开始工作。

目前一切都好。操作系统收集运行进程的无数指标,应该因此处于决定进程调度的最好位置。

实际情况

现在到了理论遭遇复杂现实的部分。我们逐渐搞清楚了这一点,操作系统认为的“最优”并不总是等于程序猿认为的“最优”。我们已经观察到,特殊情况下,多数连接由两三个进程承接 -- 特别是Linux和Solaris系统。

从操作系统的视角看,这是有道理的:上下文切换(挂起一个进程然后重新激活另一个进程)是一个相当耗资源的操作。如果有多个进程监听同一个socket,那么唤醒最近被阻塞的进程是最聪明的做法,因为执行上下文切换的几率最小。(当然,调度程序是复杂易变的讨厌鬼;上述解释必然只是实际情况的粗略概括,然而某些进程获得偏向对待的基本前提仍然有效)

不是所有的程序都有这个怪异的情况 -- 实际上多数并不会 -- 但是在有问题的那些例子中可以看到负载不均衡的现象。

确认了问题的根源后,我们有一些对应的疗法,但无一令人满意。暂时不监听这socket以便使其他工作进程有机会接受新的连接,这个办法有点儿用,但是不够。获得“特殊照顾”的工作进程接受连接的比例从90%掉到了60-70%,好了点儿,但是还不够。也不用在意这种办法对于程序处理很多短时连接的能力的显著影响。

事情越来越清晰了,就像产生随机数一样,分配连接处理工作太重要了,我们不能靠几率。经过多次讨论后我们达成了一致 -- 这根救命稻草就是直接抛弃目前的方案,整体切换到另外的方案。Node.js v0.11.2版本切换到轮询调度方案的原因就在于此:新的请求连接由主进程接受并选择一个工作进程来处理。

目前选择工作进程的算法并不深奥。就像它的名字那样,是轮询 -- 只是选中下一个可用的工作进程 -- 但是经过核心开发者和用户的测试表明,它工作的很好: 请求连接现在被分配的很平均。后面还有计划把选择算法变成可配置或者基于插件的方式。

如果你想回退到之前的任务分配方式,你可以设置cluster.schedulingPolicy:

var cluster = require(‘cluster‘);
// Set this before calling other cluster functions.
cluster.schedulingPolicy = cluster.SCHED_NONE;
cluster.fork();

或者用NODE_CLUSTER_SCHED_POLICY这个环境变量来配置:

$ export NODE_CLUSTER_SCHED_POLICY="none" # "rr" is round-robin
$ node app.js    

一次性的方法是:

$ env NODE_CLUSTER_SCHED_POLICY="none" node app.js    

关于Windows

MS Windows是仅有的将旧方法作为默认的平台。Node.js在Windows平台采用的IOCP来最大化性能。虽然多数情况下都不错,但是它使得传递连接的HANDLE对象到其他进程的成本很高。也许在libuv中可以绕过这一点,但是还不清楚这样做是否有必要:Windows的端口并没有太受到类似Linux和Solaris那样的影响。

时间: 2024-10-18 17:29:07

【译】 Node.js v0.12的新特性 -- Cluster模式采用Round-Robin负载均衡的相关文章

【译】 Node.js v0.12的新特性 -- 性能优化

Performance Optimizations性能优化 原文: https://strongloop.com/strongblog/performance-node-js-v-0-12-whats-new/ January 21, 2014/in Community, Node.js v0.12, StrongNode /by Ben Noordhuis Node.js v0.12版本如此长的研发周期(9个月并且还在继续,目前为止最长的)使得核心团队和贡献者有足够的机会来介绍一些性能优化.本

Node.js V0.12 新特性之性能优化

v0.12悠长的开发周期(已经过去九个月了,并且还在继续,是有史以来最长的一次)让核心团队和贡献者们有充分的机会对性能做一些优化. 本文会介绍其中最值得注意的几个. http://www.infoq.com/cn/articles/nodejs-v012-optimize-performance?utm_source=infoq&utm_medium=related_content_link&utm_campaign=relatedContent_articles_clk Node.js和

Node.js V0.12新特性之性能优化

v0.12悠长的开发周期(已经过去九个月了,并且还在继续,是有史以来最长的一次)让核心团队和贡献者们有充分的机会对性能做一些优化.本文会介绍其中最值得注意的几个. 支持塞住模式的可写流 现在可写流可以支持“塞住(corked)”模式,类似于你执行man tcp时见到的socket选项TCP_CORK和TCP_NOPUSH. 当被塞住时,写到流中的数据会排队直到流被重新开塞(uncorked).这样Node.js可以将比较小的写操作合并成比较大的,从而减少系统调用和TCP往返. http模块已经升

Node.js v0.12.0API手册--文件系统

File System Stability: 3 - Stable 文件系统模块是一个简单包装的标准 POSIX 文件 I/O 操作方法集.您可以通过调用require('fs')来获取该模块.文件系统模块中的所有方法均有异步和同步版本. 文件系统模块中的异步方法需要一个完成时的回调函数作为最后一个传入形参. 回调函数的构成由您调用的异步方法所决定,通常情况下回调函数的第一个形参为返回的错误信息. 如果异步操作执行正确并返回,该错误形参则为 null或者undefined. 如果您使用的是同步版

Node.js v0.12.2 手册及文档翻译

内容表 关于此文档 概要 Assertion Testing Buffer C/C++ Addons Child Processes Cluster Console Crypto Debugger DNS Domain Events File System Globals HTTP HTTPS Modules Net OS Path Process Punycode Query Strings Readline REPL Smalloc Stream String Decoder Timers

Node.js v0.10.31API手册-Domain

Node.js v0.10.31API手册-目录 Domain(域) Domains 提供了一种方式,即以一个单一的组的形式来处理多个不同的IO操作.如果任何一个注册到domain的事件触发器或回调触发了一个'error'事件,或者抛出一个错误,那么domain对象将会被通知到.而不是直接让这个错误的上下文从`process.on('uncaughtException')'处理程序中丢失掉,也不会致使程序因为这个错误伴随着错误码立即退出. 警告: 不要忽视错误! Domain error处理程序

Node.js v0.10.31API手册-事件

Node.js v0.10.31API手册-目录 Events(事件) Node里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件. 所有这些产生事件的对象都是events.EventEmitter的实例. 你可以通过require("events")来访问该模块. 通常,事件名是驼峰命名 (camel-cased) 的字符串.不过也没有强制的要求,任何字符串都是可以使用的. 为了

Node.js v0.10.31API手册-目录

这段时间,想找些新的技术学习学习,看上了Node.js,万丈高楼平地起,这就从它的API开始,然后就顺便翻译一下. Node.js v0.10.31API手册-简介 Node.js v0.10.31API手册-断言 Node.js v0.10.31API手册-Buffer Node.js v0.10.31API手册-Addons插件 持续更新中...

Node.js v0.10.31API手冊-加密

Node.js v0.10.31API手冊-文件夹 加密(Crypto) 使用 require('crypto') 来调用该模块. crypto模块提供在HTTPS或HTTP连接中封装安全凭证的方法. 它提供OpenSSL中的一系列哈希方法,包含hmac.cipher.decipher.签名和验证等方法的封装. crypto.getCiphers() 返回一个数组,包括支持的加密算法的名字. 演示样例: var ciphers = crypto.getCiphers(); console.log