Zeromq基本模型
http://www.searchtb.com/2012/08/zeromq-primer.html
http://www.kuqin.com/algorithm/20120813/328769.html
1.Request-reply模式
说明:步调一致的同步请求应答模式,发送request之后必须等待reply才能继续发送request;
Client端 一般先send request 后 receive reply;
server端 一般先 receive request后 send reply。
该模式的简单使用例程 流程如下:
Start Start
Client send the request |
Server receive the request |
Client receive the answer |
Server send the answer |
End End
a) 服务端和客户端无论谁先启动,效果是相同的,这点不同于Socket。
b) 在服务端收到信息以前,程序是阻塞的,会一直等待客户端连接上来。
c) 服务端收到信息以后,会send一个“World”给客户端。值得注意的是一定是client连接上来以后,send消息给Server,然后Server再rev然后响应client,这种一问一答式的。如果Server先send,client先rev是会报错的。
// Hello World server
// Binds REP socket to tcp://*:5555
// Expects "Hello" from client, replies with "World"
//
#include
#include
#include
#include
int main (void)
{
void *context = zmq_ctx_new ();
// Socket to talk to clients
void *responder = zmq_socket (context, ZMQ_REP);
zmq_bind (responder, "tcp://*:5555");
while (true) {
// Wait for next request from client,
//do the zmq_msg_recv before zmq_msg_send
zmq_msg_t request;
zmq_msg_init (&request);
//Receiving "Hello" from client through the responder
zmq_msg_recv (&request, responder, 0);
zmq_msg_close (&request);
// Do some 'work'
sleep (1);
// Send reply back to client
zmq_msg_t reply;
zmq_msg_init_size (&reply, 5);
memcpy (zmq_msg_data (&reply), "World", 5);
// Send "world" to the client through responder
zmq_msg_send (&reply, responder, 0);
zmq_msg_close (&reply);
}
// We never get here but if we did, this would be how we end
zmq_close (responder);
zmq_ctx_destroy (context);
return 0;
}
//
// Hello World client
// Connects REQ socket to tcp://localhost:5555
// Sends "Hello" to server, expects "World" back
//
#include
#include
#include
#include
int main (void)
{
void *context = zmq_ctx_new ();
// 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");
//only send one rquest an one reply in each process
zmq_msg_t request;
zmq_msg_init_size (&request, 5);
memcpy (zmq_msg_data (&request), "Hello", 5);
printf ("Sending Hello %d…\n", request_nbr);
zmq_msg_send (&request, requester, 0);
zmq_msg_close (&request);
zmq_msg_t reply;
zmq_msg_init (&reply);
zmq_msg_recv (&reply, requester, 0);
printf ("Received World %d\n", request_nbr);
zmq_msg_close (&reply);
zmq_close (requester);
zmq_ctx_destroy (context);
return 0;
}
2.Publish-subscribe模式
我们可以想象一下天气预报的订阅模式,由一个节点提供信息源,由其他的节点,接受信息源的信息.
说明:pub-sub是一种异步模型;
publisher向所有的subscriber push所有消息;
subscriber可以订阅多种消息,可以收到任何匹配的消息,可以过滤消息,可以向多个publisher订阅消息。
如果一个publisher没有相关的subscriber,则它只会简单的丢弃消息。
这个模型里,发布端是单向只发送数据的,且不关心是否把全部的信息都发送给订阅端。如果发布端开始发布信息的时候,订阅端尚未连接上来,这些信息直接丢弃。不过一旦订阅端连接上来,中间会保证没有信息丢失。同样,订阅端则只负责接收,而不能反馈。如果发布端和订阅端需要交互(比如要确认订阅者是否已经连接上),则使用额外的 socket 采用请求回应模型满足这个需求
// // Weather update server // Binds PUB socket to tcp://*:5556 // Publishes random weather updates // #include "zhelpers.h"
int main (void) { // Prepare our context and publisher void *context = zmq_ctx_new (); void *publisher = zmq_socket (context, ZMQ_PUB); zmq_bind (publisher, "tcp://*:5556"); zmq_bind (publisher, "ipc://weather.ipc");
// Initialize random number generator srandom ((unsigned) time (NULL)); while (true) { // Get values that will fool the boss int zipcode, temperature, relhumidity; zipcode = randof (100000); temperature = randof (215) - 80; relhumidity = randof (50) + 10;
// Send message to all subscribers char update [20]; sprintf (update, "d %d %d", zipcode, temperature, relhumidity); s_send (publisher, update); } zmq_close (publisher); zmq_ctx_destroy (context); return 0; }
// Weather update client // Connects SUB socket to tcp://localhost:5556 // Collects weather updates and finds avg temp in zipcode #include "zhelpers.h"
int main (int argc, char *argv []) { void *context = zmq_ctx_new ();
// Socket to talk to server printf ("Collecting updates from weather server…\n"); void *subscriber = zmq_socket (context, ZMQ_SUB); zmq_connect (subscriber, "tcp://localhost:5556");
// Subscribe to zipcode, default is NYC, 10001 char *filter = (argc > 1)? argv [1]: "10001 "; zmq_setsockopt (subscriber, ZMQ_SUBSCRIBE, filter, strlen (filter)); char *string = s_recv (subscriber);
int zipcode, temperature, relhumidity; sscanf (string, "%d %d %d",&zipcode, &temperature, &relhumidity);
printf ("the imformation for zipcode '%s' was %d %d %d \n", filter,zipcode,temperature,relhumidity);
zmq_close (subscriber); zmq_ctx_destroy (context); return 0; }
a) 客户端需要zmq_setsockopt (subscriber, ZMQ_SUBSCRIBE, filter, strlen (filter));设置一个过滤值,相当于设定一个订阅频道,否则什么信息也收不到。
b) 服务器端一直不断的广播中,如果中途有Subscriber端退出,并不影响他继续的广播,当Subscriber再连接上来的时候,收到的就是后来发送的新的信息了。这对比较晚加入的,或者是中途离开的订阅者,必然会丢失掉一部分信息.
c) 但是,如果Publisher中途离开,所有的Subscriber会hold住,等待Publisher再上线的时候,会继续接受信息。
3.Pipeline即 push-pull模式
想象一下这样的场景,如果需要统计各个机器的日志,我们需要将统计任务分发到各个节点机器上,最后收集统计结果,做一个汇总。PipeLine比较适合于这种场景.
说明:这个模型里,管道是单向的,从 PUSH 端单向的向 PULL 端单向的推送数据流;
Ventilator将任务分配给worker实现任务的并行处理;
Push-pull模式是单向的,很适合消费者能力不足的情况,可以提供多个消费者。一条消息如果被A消费,B将不会再消费这条消息。
可以看到,task ventilator使用的是SOCKET_PUSH,将任务分发到Worker节点上。而Worker节点上,使用SOCKET_PULL从上游接受任务,并使用SOCKET_PUSH将结果汇集到Slink。值得注意的是,任务的分发的时候也同样有一个负载均衡的路由功能,worker可以随时自由加入,task ventilator可以均衡将任务分发出去