现在的位置: 首页 > 综合 > 正文

ZeroMQ学习笔记

2013年08月23日 ⁄ 综合 ⁄ 共 2268字 ⁄ 字号 评论关闭

官网地址  http://zeromq.org

1. API

int zmq_recv (void *socket, void *buf, size_t len, int flags);

int zmq_send (void *socket, void *buf, size_t len, int flags);

zmq_recv 和 zmq_send 默认都是阻塞的,可以通过flags=ZMQ_DONTWAIT参数来设置为非阻塞模式。  buf 和 len都是靠应用程序来保证的。

对于阻塞模式,zmq_recv的返回值是接收到的字节数,注意如果超过 len,后面的数据将会被截断,但返回值的长度却是原本没有被截掉的长度。 如果错误,或者在非阻塞模式下没有消息,返回-1,并设置  errno。

2. 模式

2.1 request - reply 模式

如果server重启,那么这个模式就会失效,也就是说 server 和 client就不通消息了(可以有办法来解决这个问题,还没有看到那部分),但如果client重启的话是没有关系的。

并且一定要遵循一问一答的顺序,比如 send --receive --send ---receive ,中间如果顺序错了,比如发了两次,或收了两次就会出错,返回-1,或者先收再发也会同样出错。所以这是一个完全同步的模式,因为zmq不像socket,没有socket这个概念,如果这不同步的话就不知道是哪个连接发过来的,比如A,B都合C相连,C在监听,那么当收到数据之后,如果先收两个包放在本地,然后一个个返回,那么这个时候他就不知道要给A还是B,因为没有任何对应关系可循,这都是封装在里面的。

2.2 publish - subscribe 模式

这个模式是单向的,就是说 pub只能发, sub只能收

sub可以注册多个pub,并且多个pub上的消息会公平的过来。

如果pub没有任何sub,那么消息将会被丢弃

如果sub消费得比较慢,消息就会堆积在pub端

在v3.x版本里面,tcp 或 ipc的过滤是发生在publisher,而在低版本里面,所有的过滤都是发生在subscriber,这样就比较浪费流量和资源

sub 除了要创建
ZMQ_SUB
 类型的socket,并连接之外,还要 调用 zmq_setsockopt (subscriber, ZMQ_SUBSCRIBE,filter, strlen (filter)); 来进行注册,才有效果。 其中filter用来匹配消息开头的字符串,如果匹配则接受下来,否则丢弃;但如果filter = NULL,并且长度为0的话,则表示所有的消息都接收。 

publisher的第一个包经常是会被丢掉的,即便 sub端先起来,然后启动push 发送消息,刚开始的消息也有可能丢的  。因为即便是再快的网络,建立连接都是需要一些时间的,比如几个毫秒,而用zmq发送速度太快,在订阅者尚未与发布者建立联系时,已经开始了数据发布(内部局域网没这么夸张的),再结合之前说的如果publisher没有任何subscriber连上来的话消息会被丢弃。官网给了两个解决方案;1, 让发布者先不发数据,而是等订阅者真正连上之后,再发数据; 2,就是发布是永不停止的,没有开始与尽头的概念。

2.3  push-pull 模式

这个模式和publish-subscrible有点像,数据都是单向流动,但push-pull是基于负载均衡的任务分发,就是说如果有多个 pull端,那么push出去的数据将会平衡得分配到各个pull端,并且一旦有一个点拿到了数据,其他pull端就没有了,就是说所有的pull拿到的数据合起来才是push的数据总和。  而 publish - subscribe则是 publish的数据,所有的subscribe都可以收到,当然如果进行了过滤则只能收到符合规则的数据。

3. 其它

ZMQ的通信层,是不分哪个主动监听,哪个主动连接的,所以你可以在任意一端监听,另一端来connect。

context:一个进程只有一个context,所有的socket都在其中维护着,如果你创建了两个context,那么他们是完全独立的,一般不用这么做。 一般在开头创建context,在结尾 destory它。 如果先创建了context,然后fork了一个子进程,那么子进程会自动得到一个context,并且是和父进程独立的。

3.2 资源清理

zmq对于资源的清理是很重要的,如果有一个socket没有关闭,那么zmq_ctx_destory 将会永远挂起,即便你关闭了socket,如果有pending的连接或发送的话,zmq_ctx_destory 照样会永远挂起,除非事先设置了LINGER。 

1)尽量使用zmq_send 和 zmq_recv 从而避免使用 zmq_msg_t;

2)如果你用了 zmq_msg_recv,那么尽快调用 zmq_msg_close去清理内存(不要直接操作成员变量啊)。

3) 如果你创建了很多socket又很快用完关闭,说明你的程序可能需要重新设计,有些时候socket的handle直到destory context的时候才会被释放。 

4)在程序的结束关闭socket,并destory context

5) 在zmq_ctx_detory的时候,如果有socket还在用那么会返回错误,捕捉这个错误,并设置LINGER,并关闭socket,contex会自动被销毁。注意不要zmq_ctx_detory一个context两次。

4. 多线程问题

不要在多个线程里面使用同一个socket,就是说socket只能在一个线程里面使用。

【上篇】
【下篇】

抱歉!评论已关闭.