使用集群(recluster)扩展多线程Node.JS


发布者 kris  发布时间 1400289977725
关键字 JS学习  JavaScript 
简单的多进程服务

Gary ChambersGary Chambers

使用经典的Node.JS写的服务真的很快,一部分得益于它的非阻塞I/O接口,一部分得益于JavaScript V8引擎的特别优化。当然,其中的一个限制就是:JavaScript是单线程的,所以仅会使用到处理器的一个内核。不过,通过 recluster (集群)模块(Github: doxout/recluster, MIT) 让多核处理器发挥出它的优势是有可能的。


使用recluster


Recluster是建立在Node.JS内部集群处理能力之上的(cluster),为扩展子进程提供了一个简单的API,从而将程序扩展到所有可用的CPU核心。它也增加了一些细微的东西,像容错处理(expoenti... backoff)和热重启。

var cluster = recluster("/path/to/worker.js", {
    workers: 4,
    backoff: 10
});
cluster.run();

这段代码会产生4个工作(worker)进程,当一个子进程死掉会自动恢复(respawning)。工作进程(worker)主要负责初始化应用,绑定sockets或接口。因为这些,通常使用Node.JS的domain来封装你的应用,从而捕获异常:

var app = domain.create();
app.on("error", function(err) {
    cluster.worker.disconnect();
});
app.run(function() {
    http.createServer(function(req, res) {
        res.end("Hello World\n");
    }).listen(8000);
});


使用domain,工作进程的任何异常都会被domain的捕获,然后工作进程就可以优雅地死去了。当我们需要保证worker在一个不确定的状态下正常工作时,这通常都是一个很好的方案。


你可能会注意到,服务是在worker中实现的,而非父进程,这意味着我们要在同一个端口上绑定很多次,这明显不行,但是Node的集群机制帮我们处理了这个问题:任何在Worker代码中绑定接口或socket的操作其实都是在它的父进程中实现的;所有的工作进程都会共享同样的socket.

这样简单的处理方法使任何Node.js应用的多核布署都变得非常容易,但是这里也有限制。


状态和内存共享


Node.js操作的状态共享功能不是很多,而且没有本地的内存共享方法(注* 在进程间共享内存其实是不太安全的),但是这些限制可以在底层的灵性性上避免。

当一个应用从一个进程扩展开来时,维护状态变得越来越没有价值。在理想的状态下,应用应该是完全无状态的。客户端不确定到底是哪一个进程处理了它的请求,所以这并不总是可行的,比如:维护那些像带有用户会话状态(session)的连接,到合适的web应用这种情况。那么你该怎么办?

其实Node.js本身就开放了消息API(message),可以用来在进程间传递消息。这里有个简单的例子,这意味着所有的workers都会被通知到状态改变。

// Worker进程
http.createServer(function(req, res) {
    // do something to mutate state, and then...
    process.send("State changed!");
}).listen(8000);
// 父进程
worker.on("message", function(message) {
    // hypothetical connection pool object
    pool.broadcast(message);
});

这可能在应用非常小的时侯工作地很好,但是在应用中它不能保证状态的统一,而且很容易引起状态的不连续。如果一个worker丢失了一条广播,或者在发送消息前它被kill掉了,这样我们就会有一个不一致的状态,而且非常难以找到原因,甚至变得更难调试了。

这不是说进程内消息没什么用,但当应用扩展和变得复杂时它就会略显不足。尤其是当我们从一个单进程程序,扩展到多服务器多集群模式时,消息传递就解决不到任何问题了。

更好的选择是创建一个共享的状态代理程序。通常这个后台程序用来替换掉内存状态,我们需要一个足够快的选项:像Redis那样的。(在Node.js中启用Redis不在本文讨论的范围,但它是一个相当简单的工作)。通过这样,我们就可以有效地将“状态”从应用层转移到持久层,还能得到更好地分层这个额外的奖励。

当你的应用把状态保持的需求处理好以后,我们就可以很自信地将我们的应用发布到集群上了,然后享用我们弹性的,可扩展的,高性能构架。





回复 (2)
  • #
  • #1 redstone 1400635526000
    直接用pm2不好吗?多直接。也不用改代码
  • #2 思念生了病 1400641429000
    @redstone

    这种项目有很多, 有人喜欢轻量极的
微信扫码 立即评论