安装ZeroMQ
你可能会问,为什么不直接使用socket,而使用ØMQ呢?
因为Node.js社区信奉Unix哲学:一次只做好一件事。
Node.js贡献者们尽量保持Node.js代码的轻量、简洁。把其他更上层的事情留给开发者去解决。
使用ØMQ的好处是:
-
网络异常导致连接中断,ØMQ会自动新建连接
-
保证发送完整消息,无需处理分块发送情况
-
开销低却能包含很多必要细节,如把响应发送回正确的请求方
npm install --save --save-exact zeromq
如果你像我一样安装过程中遇到了未响应的“异常”:
Mac中npm安装zeromq时安装不成功,一直等待不结束(node scripts/prebuild-install.js || (node scripts/preinstall.js && no)
请移步该问题的处理方法。
现在安装好了,开始写代码吧!
各个模式
发布者(pub)/订阅者(sub)模式
其实就类似于广播电台,电台总部相当于发布者,负责向外发送数据,订阅者相当于FM收音机,设定固定频率收到广播信号。
Pub/Sub
This example demonstrates using zeromq
in a classic Pub/Sub, Publisher/Subscriber, application.
Publisher: pubber.js
// pubber.js
var zmq = require("zeromq"),
sock = zmq.socket("pub");
sock.bindSync("tcp://127.0.0.1:3000");
console.log("Publisher bound to port 3000");
setInterval(function() {
console.log("sending a multipart message envelope");
sock.send(["kitty cats", "meow!"]);
}, 500);
Subscriber: subber.js
// subber.js
var zmq = require("zeromq"),
sock = zmq.socket("sub");
sock.connect("tcp://127.0.0.1:3000");
sock.subscribe("kitty cats");
console.log("Subscriber connected to port 3000");
sock.on("message", function(topic, message) {
console.log(
"received a message related to:",
topic,
"containing message:",
message
);
});
请求(rep)/响应(req)模式
先有请求进来,再有响应出去,一个接一个,如果有更多请求进来,就会进入到请求队列,由ZeroMQ依次处理。应用程序每次只处理一个请求。
每次结点只处理一个事件,没有并行处理的能力,因此这种模式不适合对性能要求较高的场景。
Rep/Req
rep.js
//rep.js
const zmq = require('zeromq');
const responder = zmq.socket('req');
responder.on('message',data=>{
//接受请求的数据
console.log(data);
//向外响应数据
responder.send('响应器的数据');
});
responder.bind('tcp://127.0.0.1:60401',error => {
console.log('Listening for zmq requesters')
});
req.js
//req.js
const zmq = require('zeromq');
const requester = zmq.socket('req');
requester.on('message',data=>{
//处理响应的内容
console.log(data);
});
requester.bind('tcp://127.0.0.1:60401');
requester.send('发送请求');
推送(push)/拉取(pull)模式
这个就相当于github上push和pull,你给我,或者我给你,并不返回数据。
但你有个任务队列,当你要把任务分给多个工作进程时,这个模式就非常适用。
Push/Pull
This example demonstrates how a producer pushes information onto a socket and how a worker pulls information from the socket.
producer.js
// producer.js
var zmq = require("zeromq"),
sock = zmq.socket("push");
sock.bindSync("tcp://127.0.0.1:3000");
console.log("Producer bound to port 3000");
setInterval(function() {
console.log("sending work");
sock.send("some work");
}, 500);
worker.js
// worker.js
var zmq = require("zeromq"),
sock = zmq.socket("pull");
sock.connect("tcp://127.0.0.1:3000");
console.log("Worker connected to port 3000");
sock.on("message", function(msg) {
console.log("work: %s", msg.toString());
});
ROUTER/DEALER模式
ROUTER socket可以简单的理解为Rep socket并发版。
DEALER 可以理解为并行的req,可以并行发出多个请求。
const router = zmq .socket('router');
const dealer = zmq.socket('dealer');
router.on('message',(...frames)=>dealer.send(frames));
dealer.on('message',(...frames)=>router.send(frames));
这种方式,我们创建了一个传输管道,如图所示的消息传输架构。
方框代表我们开发的Node.js程序。首先REQ socket连接到ROUTER,当REQ发出请求时,ROUTER把这个请求转发给DEALER,然后DEALER从它的连接的REP中选择一个,并把请求发送过去。
当REP产生响应信息时,则按照相反的路径把消息返回回去。当DEALER接收到应答消息时,把它转发给ROUTER,然后ROUTER根据消息的帧记录来源数据决定返回给哪个REQ。
Node.js集群
cluster模块
可以使用Node.js内置的cluster模块创建子进程,这个过程称为clustering,创建合适数量的Node.js进程可以充分利用多核CPU的资源。
引入cluster模块,可以通过fork()创建工作进程,通过一系列事件和主进程通信即可。
图片解释了集群中各个部分是如何分工的,中间方框表示 Node.js进程,椭圆形表示socket绑定的资源,连接线指明了各个socket与节点之间的连接关系。
简单来说,就是客户端访问服务器数据时,不再仅限一个服务器主进程,分发到了各个工作进程去处理事件,应用程序实现了多进程工作分配的功能,主进程也起到了负载均衡的作用。