写点什么

Node.js V0.12 新特性之 Cluster 轮转法负载均衡

2014 年 3 月 17 日

回顾 Node 内置的 cluster 模块

Node.js 固有的单线程模型经常被认为是它的一个软肋。不管你的机器上有多少 CPU 内核,Node.js 能用上的也仅仅是其中之一(某些操作会被有条件地卸载到线程池中。大多数程序只是在 CPU 的总时间上分了一杯羹,所以更好地利用可用的处理能力并不能起到多大作用)。

所以 Node.js 从 v0.8 开始,新增加了一个内置的‘cluster’模块。你可以用 cluster 模块设置一个主进程作为管理者,由一或多个工人进程完成实际工作。

让创建“发完就忘”的多进程服务器变得更容易是其目标之一。在完美的世界中,你应该可以一行代码都不用改,就能让一个已有的单进程程序繁衍出任意多的工人进程。

当然,事情不可能那么容易,但对于没有共享状态,或共享状态很少,或把共享状态存放在数据库或 Web 服务之类的外部资源中的程序而言,cluster 模块的确让这件事变得简单直接了。把那样的程序变成集群化的通常只需要几行代码:

复制代码
var cluster = require('cluster');
var os = require('os');
if (cluster.isMaster)
// 繁衍工人进程,数量跟系统中的 CPU 数量一样
for (var i = 0, n = os.cpus().length; i < n; i += 1)
cluster.fork();
else
// 启动程序
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 中–大多数连接最终都落在了两或三个进程里。

从操作系统的角度来看这是可以理解的:上下文切换(挂起一个进程,然后重新激活另一个)是相当昂贵的操作。如果你有 n 个进程全都等在同一个 socket 上,那么唤醒最近被阻塞的进程是明智之举,因为那样可以最大限度地避免上下文切换。(当然,调度器是一种复杂而又多变的野兽;上面只是对真实情况泛泛的解释。基本前提是那些得到优待的进程会仍然受到优待)。

并不是所有的程序都会受到这个怪癖的影响,实际上大多数都不会,但那些确实会受到影响的会出现非常不均衡的负载。

一旦确定了根本原因,缓解措施就可以用上了。但还没有特别令人满意的。比如暂时放弃监听 socket 以便让其它工人进程有机会接受新连接,这有点儿用,但还不够。‘选定几个’中的连接数从 90% 下降到了 60-70%,有改善,但还是不够好。更别提它对那些要处理非常短命的连接的程序的剧烈影响了。

更重要的是,我们清楚地意识到,就像随机数的生成一样,接入连接的分配太重要了,不能靠运气。经过多次讨论,我们达成了共识 - 我们最后,也是最好的希望是可以简单地抛弃目前的做法,切换到完全不同的东西上。这就是 Node.js v0.11.2 中的 cluster 模块换成了 round-robin 方式的原因,新连接由主进程接受,然后由它选择一个工人进程把连接交出去。

现在这个选择工人进程的算法还不是特别精巧。就像它的名字一样,它用的是轮转法– 只是拿起下一个可用的工人进程– 但经过核心开发人员和用户的测试,证明它很好用:连接在工人进程之间分配得很均衡。我们正在考虑将选定的算法变成可以由开发人员配置或插入的东西。

如果你还想用老办法分配连接,可以在程序中设定 cluster.schedulingPolicy:

复制代码
var cluster = require('cluster');
// 在调用其他 cluster 函数前设定这个
cluster.schedulingPolicy = cluster.SCHED_NONE;
cluster.fork();

或者通过环境变量NODE_CLUSTER_SCHED_POLICY调整调度策略:

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

如果不想影响你的 shell 环境,就放在一行命令里:

复制代码
$ env NODE_CLUSTER_SCHED_POLICY="none" node app.js

Windows 上的注意事项

MS Windows 是默认使用老办法的唯一平台。为了达到性能最优,Node.js 在 Windows 上使用了 IOCP 。尽管这在大多数情况下都不错,但这样将 HANDLE 对象 (连接) 发送给其它进程代价十分高昂。 尽管有可能在 libuv 中解决这个问题,但我们还不清楚是否真的有必要这么做:Windows 的端口几乎不会受到负载均衡问题的影响,而 Linux 和 Solaris 的端口确实会受影响。

作者简介

本文最初由 Ben Noordhuis 发表在 StrongLoop 上。Ben Noordhuis 从 2010 年就跟着 Ryan Dahl 开发 Node.js 的核心代码。他一直在为改进 Node 核心代码而努力做着编码、调试和基准测试等工作。作为最高产的 Node 核心开发者之一,Ben 编写了 Node.js 和 libuv 中的很多代码。 StrongLoop 降低了在 Node 中开发APIs 的难度,还添加了监测、集群化以及私有注册的支持等DevOps 能力。

原文英文链接: What’s New in Node.js v0.12: Cluster Round-Robin Load Balancing

2014 年 3 月 17 日 05:509699
用户头像

发布了 45 篇内容, 共 22.3 次阅读, 收获喜欢 3 次。

关注

评论

发布
暂无评论
发现更多内容

阿里云视频云技术专家 LVS 演讲全文:《“云端一体”的智能媒体生产制作演进之路》

阿里云视频云

媒体 音视频

USDT支付系统开发方案,区块链币支付系统搭建

135深圳3055源中瑞8032

【Knative系列】一文读懂 Knative Serving扩缩容的原理

Chumper

Serverless knative autoscaler kantive

响应式编程简介之:Reactor

程序那些事

响应式编程 reactor Reactive 程序那些事 响应式系统

直播预告 | 应用加固防破解,4.1折就够了

蚂蚁集团移动开发平台 mPaaS

安全攻防 App风险 mPaaS

架构师第一期作业(第 7 周)

Cheer

课程作业

天源迪科受邀出席“第四届央企电商化采购发展高峰论坛"

DT极客

Week 5命题作业

balsamspear

极客大学架构师训练营

英特尔首批独显笔记本亮相,非凡S3x纵享轻薄新体验

intel001

还在为算法烦恼?那你应该还没看过这份Git上70k标星的笔记

Java架构师迁哥

交易所开发技术,数字货币交易系统源码搭建

135深圳3055源中瑞8032

书写高质量SQL的30条建议

诸葛小猿

MySQL SQL优化

darknet A版安装

Dreamer

Dubbo-go Server端开启服务过程

apache/dubbo-go

dubbo dubbo-go dubbogo

区块链数字货币钱包开发模式,深圳数字钱包开发

135深圳3055源中瑞8032

区块链+能源 大放异彩

CECBC区块链专委会

区块链 能源

银行数字化转型:需建立起以体验为核心、数据为基础、技术为驱动的架构体系

CECBC区块链专委会

银行 数字经济

Week 7 命题作业

阿泰

手动造轮子——为Ocelot集成Nacos注册中心

yi念之间

nacos ASP.NET Core Ocelot

架构师训练营第一期 - week7

习习

央行数字货币为人民币国际化之路提供推动力

CECBC区块链专委会

数字货币

「混合云」会是云计算的下一个战场吗?

ToB行业头条

阿里云

广东云算力矿机系统开发,挖矿平台搭建

135深圳3055源中瑞8032

在深夜加油站遇见哈利波特

脑极体

high-performance-tidb-challenge 记录

王传义

DDIA 读书笔记(5)数据分区方案

莫黎

读书笔记

手动造轮子——基于.NetCore的RPC框架DotNetCoreRpc

yi念之间

RPC ASP.NET Core

甲方日常 45

句子

工作 随笔杂谈 日常

Android 一行代码接入扫码功能 (CameraX + zxing)

Java android kotlin zxing camera

【性能优化】纳尼?内存又溢出了?!是时候总结一波了!!

冰河

性能优化 内存泄露 高并发 高性能 内存溢出

Week 5学习总结

balsamspear

极客大学架构师训练营

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

Node.js V0.12新特性之Cluster轮转法负载均衡-InfoQ