`
hongtoushizi
  • 浏览: 370537 次
  • 性别: Icon_minigender_1
  • 来自: 天津
社区版块
存档分类
最新评论

高并发下的Node.js与负载均衡

阅读更多

转载自: http://www.cnblogs.com/tingshuo/archive/2013/01/17/2864280.html

 

新兴的Node.js已经吸引了很多开发人员的眼光,它提供给我们一个快速构建高性能的网络应用的平台。我也开始逐步投入node.js的怀抱,在学习和使用的过程中,遇到了一些问题,也有一些经验,我觉得有必要写出来,作为总结,也用作分享。

众所周知,node.js基于v8引擎,所以它本身并不支持多线程(有多线程的Module哦),那么为了充分利用server的Multi-core,就必须使用多进程的方式。那么进程之间如何负载均衡就会是一个关键所在。

多进程共享监听socket

Node.js与进程相关的模块有processchild_processcluster,这其中cluster用于方便的创建共享端口的多进程模式(The cluster module allows you to easily create a network of processes that all share server ports),这种模式使多个进程间共享一个监听状态的socket,并由系统将accept的connection分配给不同的子进程,而且实现起来也非常简单,cluster为你做了大部分事情,这里有一个test case:

复制代码
 1 var cluster = require('cluster');
 2 var http = require('http');
 3 var numCPUs = require('os').cpus().length;
 4 
 5 if (cluster.isMaster) {
 6   // Fork workers.
 7   for (var i = 0; i < numCPUs; i++) {
 8     cluster.fork();
 9   }
10 
11   cluster.on('exit', function(worker, code, signal) {
12     console.log('worker ' + worker.process.pid + ' died');
13   });
14 } else {
15   // Workers can share any TCP connection
16   // In this case its a HTTP server
17   http.createServer(function(req, res) {
18     res.writeHead(200);
19     res.end("hello world\n");
20   }).listen(8000);
21 }
复制代码

但是这种完全依赖于系统的负载均衡存在着一个重要缺陷:在linux和Solaris上,只要某个子进程的accept queue为空(通常为最后创建的那个子进程),系统就会将多个connetion分配到同一个子进程上,这会造成进程间负载极为不均衡。特别是在使用长连接的时候,单位时间内的new coming connection并不高,子进程的accept queue往往均为空,就会导致connection会不停的分配给同一个进程。所以这种负载均衡完全依赖于accept queue的空闲程度,只有在使用短连接,而且并发非常高的情况下,才能达到负载均衡,但是这个时候系统的load会非常高,系统也会变得不稳定起来。

Nginx是怎么做的?

如果你了解nginx,那么你可能第一时间会想到使用nginx的处理方式,nginx有一个master和多个worker进程,master进程监听端口,负责accept connection,并把accept 的socket发送给各worker进程,由worker进程接收数据并处理。linux下,nginx是使用socketpair建立master和worker进程间的通信,并使用sendmsgrecvmsg等api来传输命令和文件描述符的。那么node.js是否支持这种方案呢?

答案是肯定的,作出这个回答的依据在于node.js的child_process和cluster模块均有一个send方法:child.send(message, [sendHandle])

这个方法的第二个参数就是我们想要传递的socket,而且node.js文档上还给出了一个test case:

复制代码
 1 var normal = require('child_process').fork('child.js', ['normal']);
 2 var special = require('child_process').fork('child.js', ['special']);
 3 // Open up the server and send sockets to child
 4 var server = require('net').createServer();
 5 server.on('connection', function (socket) {
 6   // if this is a VIP
 7   if (socket.remoteAddress === '74.125.127.100') {
 8     special.send('socket', socket);
 9     return;
10   }
11   // just the usual dudes
12   normal.send('socket', socket);
13 });
14 server.listen(1337);
复制代码

child.js

1 process.on('message', function(m, socket) {
2   if (m === 'socket') {
3     socket.end('You were handled as a ' + process.argv[2] + ' person');
4   }
5 });

简单,精炼!似乎是一个完美的解决方案。我们稍微加工一下,让他成为一个可以正常运行的http server:

master.js

复制代码
 1 var http = require('http'),
 2     numCPUs = require('os').cpus().length;
 3     cp = require('child_process'), 
 4     net = require('net');
 5 var workers = [];
 6 for (var i = 0; i < numCPUs; i++) {
 7     workers.push(cp.fork('app.js', ['normal']));
 8 }
 9 
10 net.createServer(function(s) {
11     s.pause();
12     var worker = worker.shift();
13     worker.send('c',s);
14     workers.push(worker);
15 }).listen(80);
复制代码
复制代码
 1 var http = require('http'),
 2     cp = require('child_process'),
 3     net = require('net');
 4 var server = http.createServer(function(req,res){
 5     res.writeHead(200, {"Content-Type": "text/plain",         "Connection": "close"});
 6     res.end("hello, world");
 7 });
 8 console.log("webServer started on " + process.pid);
 9 process.on("message", function(msg,socket) {
10     process.nextTick(function(){
11         if(msg == 'c' && socket) {
12             socket.readable = socket.writable = true;
13             socket.resume();
14             server.connections++;
15             socket.server = server;
16             server.emit("connection", socket);
17             socket.emit("connect");
18         }
19     });
20 });
21                                         
复制代码

我们在worker进程中创建了一个http server,但是这个http server并不监听,也不绑定端口,在收到master传输过来的socket时,调用server.emit("connection", socket);就可以触发server的connection事件了。看起来很不错,简单的测试之后可以正常工作,这个方案几近于完美。在经历过共享监听socket方案的失败后,我们把服务迁移到这种架构上来。

但是,我们遇到了问题。 我们发现master进程的cpu和内存在逐渐增长,并最终到达100%,或者node.js崩溃(Assertion `fd_to_send >= 0' failed),这令我很抓狂,百般无奈之下我们求助于node.js的开发人员,在github上报告了我们遇到的问题(Issue #4587)。就在当天晚上,node.js的开发人员indutny找到了问题所在,主要在于主进程在将socket发送给子进程之后,并没有销毁,而是保留在socketList中,这会导致socket在主进程中逐步累积,并最终达到上限。

indutny很快解决了这个问题,于第二天提交了这个commit,按照这个commit,indutny给send函数增加了第三个可选参数,修改后的send函数将变为:

child.send(message,[socket], [{ track: false, process: false }])

我们的master进程不需要track每个socket状态,所以我们将它设为false即可。到此,这个问题得到了完美的解决,希望这个改进可以随node.js的下一个版本一起发布。

分享到:
评论

相关推荐

    基于Node.js的高并发电商购物系统设计与实现

    【基于Node.js的高并发电商购物系统设计与实现】 在当今互联网时代,电子商务网站面临着日益增长的并发访问压力,特别是由于直播营销和各种电商节日活动的兴起,这使得企业对高性能、高并发的系统需求更加迫切。...

    基于 OpenResty 和 Node.js 的微服务架构实践.pdf

    例如,OpenResty 作为前端网关处理静态资源请求、负载均衡、安全过滤等,而 Node.js 用于后端服务的开发,处理复杂的业务逻辑。 ### 四、总结 基于 OpenResty 和 Node.js 的微服务架构设计具有高度灵活性和扩展性...

    Node.js 实现的 MySQL 分表分库中间件,用于海量数据的分布式集群储存管理和高并发访问。.zip

    总之,Node.js实现的MySQL分库分表中间件是应对大数据挑战的有效工具,它利用了Node.js的并发处理能力和MySQL的成熟数据库功能,为开发高并发、高性能的分布式应用提供了可能。 TribeDB-master 提供的源码可能为...

    高可用企业级Node.js应用视频教程下载

    1. **基础概念**:首先,我们会回顾Node.js的基础知识,包括模块系统、异步编程模型以及V8引擎的优势,以便理解Node.js在构建高并发应用中的独特之处。 2. **设计模式**:在企业级应用中,良好的架构设计至关重要。...

    Professional Node.js 高级编程 (英文pdf+源码)

    3. **非阻塞I/O**:Node.js通过非阻塞I/O模型实现了高并发,书中会讲解这一特性背后的原理及其在实际应用中的实现方式。 4. **网络编程**:包括HTTP服务器的创建、TCP套接字编程,以及WebSocket等实时通信协议的...

    packt Node.JS Cookbook 2nd with source code

    Node.js采用了事件驱动、非阻塞I/O模型,使其在处理高并发请求时表现出色,尤其适合构建实时的、协作式的Web应用。 本书"Node.JS Cookbook"的核心内容可能包括以下几个方面: 1. **基础概念**:介绍Node.js的基础...

    Node.js-Node.js性能平台-Node.jsPerformancePlatform

    **alinode** 是一个高性能的Node.js运行时环境,它是基于V8引擎的,但针对Node.js进行了优化,特别是在处理高并发场景时,能显著提升性能。alinode的主要特点包括更快的启动速度、更好的CPU和内存使用效率,以及对大...

    Node.js-使用Node.js和WebSockets实现的聊天App

    Node.js作为一个基于Chrome V8引擎的JavaScript运行环境,因其异步、事件驱动的特性,非常适合处理高并发的实时数据交换。而WebSockets协议则为双向通信提供了标准,使得服务器和客户端可以持续保持连接状态,实现...

    Node.js-Node.JSMySQL实现的B2C购物系统

    10. **性能优化**:包括数据库索引优化、缓存策略、异步处理、负载均衡等,以确保系统在高并发情况下的稳定性和响应速度。 11. **安全性**:学习如何防止SQL注入、XSS攻击、CSRF攻击等,确保系统安全。 12. **测试...

    node.js+mysql博客全栈系统源码.zip

    其主要特点包括事件驱动、非阻塞I/O以及模块化结构,特别适合处理大量并发连接,使得开发高并发、实时交互的应用变得轻而易举。 二、MySQL数据库基础 MySQL是一款开源、免费的关系型数据库管理系统,广泛应用于Web...

    Node.js-高质量Node.js微服务的编写和部署

    10. **负载均衡**:Nginx或Traefik可以作为反向代理和负载均衡器,将请求分发到多个微服务实例,提高系统整体的处理能力。 以上就是"高质量Node.js微服务的编写和部署"涉及的一些核心知识点,涵盖了从微服务设计、...

    Node.js-Nodejs学习笔记以及经验总结

    Node.js是一种基于Chrome V8引擎的JavaScript运行环境,它允许开发者使用JavaScript进行服务器端编程,打破了JavaScript只能在浏览器中运行的传统。本学习笔记和经验总结将深入探讨Node.js的核心概念、主要特性、...

    基于Node.js与微信小程序的活动管理平台.zip

    它的异步I/O模型使得它在处理高并发请求时表现出色,适合构建实时通信应用,如聊天系统、流媒体服务或API服务器。 2. **Express框架**:Node.js领域中最常用的Web应用框架,简化了HTTP服务器的创建,提供了路由、...

    源码&笔记_Node.js_node.js相关_前端学习_

    Node.js 的核心特性之一就是其非阻塞、事件驱动的 I/O 模型,这使得它在处理高并发请求时表现出色。V8 引擎提供了快速的 JavaScript 执行速度,使得 Node.js 成为构建高性能网络应用的理想选择。 2. **模块系统** ...

    Node.js-Node.js的分布式计算框架

    Node.js作为一个基于Chrome V8引擎的JavaScript运行环境,以其异步、非阻塞I/O模型而闻名,使得它在处理高并发场景时表现出色。在分布式计算领域,Node.js也提供了一系列工具和框架,帮助开发者构建可扩展的、高效的...

    node.js.zip下载

    这种模式使得Node.js能够处理大量并发连接,是构建高并发服务器的理想选择。 Node.js的应用场景非常广泛,包括但不限于: 1. 实时应用:如聊天室、实时数据分析等,得益于其高效的I/O处理能力。 2. API服务器:Node...

    Node.js-基于RubbitMQ的node微服务框架

    Node.js以其非阻塞I/O和事件驱动的特性,使得它在处理高并发场景下表现出色。它支持快速构建可伸缩的网络应用程序,是构建微服务的理想选择。通过模块化的设计,开发者可以轻松地创建和维护各个独立的服务,每个服务...

    Apress.Pro.Node.js.for.Developers.Nov.2013.rar

    7. **性能优化**:由于Node.js的非阻塞特性,它在处理高并发请求时表现出色。这部分会讨论性能监控、调试技巧和最佳实践,以确保应用程序的高效运行。 8. **测试与部署**:编写高质量的代码需要良好的测试策略。书...

    node 12.14.1

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它让开发者能够在服务器端使用 JavaScript 进行编程。版本号 12.14.1 是 Node.js 的一个重要稳定版本,发布于2019年12月,包含了一些关键的更新和改进。 ...

Global site tag (gtag.js) - Google Analytics