今天领导又跑来问我在UOS上面有没有部署过ZMQ说是一个业务部门要使用,问我们要适配过的版本,好无语ZMQ没有听说过,在MQ听我知道“兔子”,怎么又冒出来一个ZMQ,没有办法只能先查询一下。
先简单解释一下ZMQ,ZeroMQ是宣称通信最快的队列。它有三个基本的通信模型,分别是“Request-Reply “,”Publisher-Subscriber“,”Parallel Pipeline”
Request-Reply模型

简单理解一下就是请求应答模式,由 Client 发起请求,并等待 Server 回应请求。请求端发送一个简单的 hello,服务端则回应一个 world。默认是一问一答模式的,不能同时send多个数据,只能srsrsr(s指发送,r指接收)这样发送接收。
还有一种特殊的Request-Reply模型,它在Request-Reply模型上加了一层router-dealer,如下图所示:

router-dealer分为ZMQ_DEALER和ZMQ_ROUTER,它是一个代理层。它对收到的消息公平排队,并以RR方式发送消息,在遇到异常时发生阻塞。它的主要作用是将收到的请求load balance地发送给到worker们。ZMQ_ROUTER 收到消息时会在消息栈上加一层包含消息来源地址的消息;发送消息时,会将这一层消息取出,将其作为发送的目的地。如果发送时遇到异常,则丢弃消息。
服务端和客户端无论谁先启动,效果是相同的。
Request-Reply与router-dealer的区别:
- 对于Request类型的socket,它是同步的,它一个时刻只能对一个连接进行操作,在一个连接上发送了数据之后,必须接着在这个连接上执行recv,也就是send与recv必须同时匹配出现;
- Response类型的socket也是同步的,与Request的意思差不多,不过顺序是先recv再send;
- Router类型的socket是异步的,他可以在随时执行recv与send,而不必在同一时刻必须要强制在某个连接上进行操作。它会根据标志帧来具体的区分应该在哪一个链接上进行操作
- Dealer类型的socket,异步,将收到的请求load balance地发送出去
Publisher-Subscriber模式
发布订阅模型。这个模型里,发布端是单向只发送数据的,且不关心是否把全部的信息都发送给订阅者。如果发布端开始发布信息的时候,订阅端尚未连接上,这些信息直接丢弃。不过一旦订阅端连接上来,中间会保证没有信息丢失。订阅端则只负责接收,而不能反馈。
如果发布端和订阅端需要交互(比如要确认订阅者是否已经连接上),则使用额外的socket采用请求回应模型满足这个需求。

Parallel Pipeline模式
管道模型。这个模型里,管道是单向的,从PUSH端单向的向PULL端单向的推送数据流。这种模式与pub/sub模式一样都是单向的,区别有两点:
- 该模式下在没有消费者的情况下,发布者的信息是不会消耗的(由发布者进程维护);
- 多个消费者消费的是同一列信息,假设A得到了一条信息,则B将不再得到。

另外关于部署,因为没有rpm包,只能通过源码编译了:
安装需要源码编译软件包:
#dnf install gcc-c++ make libtool -y
#wget https://github.com/zeromq/libzmq/releases/download/v4.3.4/zeromq-4.3.4.tar.gz
#tar -xzf zeromq-4.3.4.tar.gz
#cd zeromq-4.3.4
#./configure
#make&&make install
另外如果是pyhon 和C库需要用的话需要在单独安装和编译zmq的库
安装Python ZMQ的库,我选择的偷懒方式
#yum install python3-devel
#pip3 install pyzmq
最后的验证就方案的,为了简单就用python的方式验证了
Python 示例 - 请求/响应模式
server.py
import threading
context = zmq.Context()
frontend = context.socket(zmq.XREP)
frontend.bind("tcp://127.0.0.1:5559")
backend = context.socket(zmq.XREQ)
backend.bind("tcp://127.0.0.1:5560")
def func(context):
socket = context.socket(zmq.REP)
socket.connect("tcp://127.0.0.1:5560")
while True:
msg = socket.recv()
rsp = "ack : %s" % msg
socket.send(rsp)
for i in range(6):
t = threading.Thread(target = func, args = (context,))
t.start()
zmq.device(zmq.QUEUE, frontend, backend)
frontend.close()
backend.close()
context.term()
client.py
import zmq
import sys
context = zmq.Context()
inter = "tcp://127.0.0.1:5559"
def func(interface):
i = 1
while True:
socket = context.socket(zmq.REQ)
socket.connect(interface)
socket.setsockopt(zmq.LINGER, 0)
socket.setsockopt(zmq.RCVTIMEO , 1000)
msg = "msg %s" % (i)
ret = socket.send(msg)
i += 1
try:
msg_in = socket.recv()
print "Recving" , msg_in
except zmq.ZMQError , diag:
ret = str(diag)
print "ZMQError : " , ret
continue
socket.close(True)
# context.term()
if __name__ == "__main__":
func(inter)
其中的zmq.LINGER属性会给指定的socket设定关闭前的停留时间。停留时间指定了在对一个socket调用zmq_close(3)函数之后,这个socekt上即将被发送但还没有被发送到对端的消息在内存中继续停留的时间。属性值 -1表示无限的停留时间。还没被发送的消息在socket调用zmq_close()操作之后不会被丢弃;试图使用zmq_term()操作对context进行终结的操作会被阻塞,直到所有没有被发送的消息被发往对端为止。属性值0 表示没有停留时间。当使用zmq_close()函数将socket关闭的时候,所有没有被发送呃消息都会被丢弃。属性是正数值表示设置一个毫秒为单位的停留时间。在第socket调用zmq_close()操作后,还没有发送的消息不会被丢弃;试图使用zmq_term()对于此scoket相关联的context进行终结的时候会被阻塞,直到所有未被发送的消息都被发往对端;或者停留时间已经到达,此时所有未发送的消息都会被丢弃。
其中的zmq.RCVTIMEO 设置socket的接收操作超时时间。如果属性值是0,zmq_recv(3)函数将会立刻返回,如果没有接收到任何消息,将会返回EAGAIN错误。如果属性值是 -1,将会阻塞,直到接收到消息为止。对于任何其它值,都会进行等待这么多时间,直到返回EAGAIN错误。
好了就这样把,不我想知道现在还有人用ZMQ么~
1376

被折叠的 条评论
为什么被折叠?



