史上最快消息内核——ZeroMQ
ZeroMQ是一个很有个性的项目,它原来是定位为“史上最快消息队列”,所以名字里面有“MQ”两个字母,但是后来逐渐演变发展,慢慢淡化了消息队列的身影,改称为消息内核,或者消息层了。从网络通信的角度看,它处于会话层之上,应用层之下,有了它,你甚至不需要自己写一行的socket函数调用就能完成复杂的网络通信工作。
借用官方的例子:
客户端(发送N个“Hello”消息到服务端,接受回应):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
// // Hello World client // Connects REQ socket to tcp://localhost:5555 // Sends "Hello" to server, expects "World" back // #include <zmq.h> #include <string.h> #include <stdio.h> #include <unistd.h> int main () { void *context = zmq_init (1); // Socket to talk to server printf ( "Connecting to hello world server...\n" ); void *requester = zmq_socket (context, ZMQ_REQ); zmq_connect (requester, "tcp://localhost:5555" ); int request_nbr; for (request_nbr = 0; request_nbr != 10; request_nbr++) { zmq_msg_t request; zmq_msg_init_data (&request, "Hello" , 6, NULL, NULL); printf ( "Sending request %d...\n" , request_nbr); zmq_send (requester, &request, 0); zmq_msg_close (&request); zmq_msg_t reply; zmq_msg_init (&reply); zmq_recv (requester, &reply, 0); printf ( "Received reply %d: [%s]\n" , request_nbr, ( char *) zmq_msg_data (&reply)); zmq_msg_close (&reply); } zmq_close (requester); zmq_term (context); return 0; } |
服务端(接收客户端的消息,返回“World”给客户端):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
// // Hello World server in C++ // Binds REP socket to tcp://*:5555 // Expects "Hello" from client, replies with "World" // #include <zmq.hpp> #include <unistd.h> #include <stdio.h> #include <string.h> int main () { // Prepare our context and socket zmq::context_t context (1); zmq::socket_t socket (context, ZMQ_REP); socket.bind ( "tcp://*:5555" ); while ( true ) { zmq::message_t request; // Wait for next request from client socket.recv (&request); printf ( "Received request: [%s]\n" , ( char *) request.data ()); // Do some 'work' sleep (1); // Send reply back to client zmq::message_t reply (6); memcpy (( void *) reply.data (), "World" , 6); socket.send (reply); } return 0; } |
一个套接字相关的调用都没有,一个网络程序就写好了,生活真美好啊。
与其他的消息队列相比,ZeroMQ有以下一些特点
1.点对点无中间节点。
传统的消息队列都需要一个消息服务器来存储转发消息。而ZeroMQ则放弃了这个模式,把侧重点放在了点对点的消息传输上,并且(试图)做到极致。以为消息服务器最终还是转化为服务器对其他节点的点对点消息传输上。ZeroMQ能缓存消息,但是是在发送端缓存。ZeroMQ里有水位设置的相关接口来控制缓存量。当然,ZeroMQ也支持传统的消息队列(通过zmq_device来实现)。
2.强调消息收发模式。
在点对点的消息传输上ZeroMQ将通信的模式做了归纳,比如常见的订阅模式(一个消息发多个客户),分发模式(N个消息平均分给X个客户)等等。下面是目前支持的消息模式配对,任何一方都可以做为服务端。
- PUB and SUB
- REQ and REP
- REQ and XREP
- XREQ and REP
- XREQ and XREP
- XREQ and XREQ
- XREP and XREP
- PUSH and PULL
- PAIR and PAIR
3.以统一接口支持多种底层通信方式(线程间通信,进程间通信,跨主机通信)。
如果你想把本机多进程的软件放到跨主机的环境里去执行,通常要将IPC接口用套接字重写一遍。非常麻烦。而有了ZeroMQ就方便多了,只要把通信协议从"ipc:///xxx"改为
"tcp://*.*.*.*:****"就可以了,其他代码通通不需要改,如果这个是从配置文件里读的话,那么程序就完全不要动了,直接复制到其他机器上就可以了。以为ZeroMQ为我们做了很多。
4.异步,强调性能。
ZeroMQ设计之初就是为了高性能的消息发送而服务的,所以其设计追求简洁高效。它发送消息是异步模式,通过单独出一个IO线程来实现,所以消息发送调用之后不要立刻释放相关资源哦,会出错的(以为还没发送完),要把资源释放函数交给ZeroMQ让ZeroMQ发完消息自己释放。
目前ZeroMQ还不是非常成熟(本文写作时最新版是2.0.10版),设计上还有一点点小缺陷,比如不能得到客户端的IP,丢消息等。不过,开发很活跃,很有潜力。另外,ZeroMQ配合Protocol buffer使用真是绝了。
官方的教程写的非常详细:http://zguide.zeromq.org/chapter:all大家可以去看看。