ZeroMQ笔记

ZeroMQ是轻量的消息中间件,与其它常见的消息服务,如ActiveMQ、 MQTT等显著不同 的是,

  1. 它不需要安装服务器,通常只需要使用者安装libmq库并引入相应程序语言的封装库就可以使用。
  2. 另一个显著区别是,ZeroMQ不明确要求通信双方的哪一方作为服务端,例如 在一个PUB/SUB通信组合中,SUB既可作为服务端也可作为客户端存在。
  3. 在ZeroMQ的客户端可以先于服务端启动,这在其它消息服务中间件中是不允许的。
  4. 用户可组合ZeroMQ的消息模式(即DEALER/ROUTER,PUB/SUB,REQ/REP等等), 定制其它消息中间件难以实现的业务逻辑

本文简单介绍ZeroMQ的特性及一些常见使用模式。

关于ZeroMQ的数据包

ZeroMQ的使用定长数据包,即[data-len][data][data]...的形式传输数据。 用户通常需要在此基础上自定义序列化协议(如纯字符串、JSON、ProtoBuf等)。

ZeroMQ支持的底层传输协议类型

在创建socket时需要指定协议类型,支持的类型为:

  • 单播:
    • inproc: 多用于应用内进程间的一对一通信;
    • ipc:可用于同一服务器上不同应用间通信
    • tcp:基于TCP的通信方式
  • 多播:epgm, pgm

ZeroMQ 的消息类型组合

ZeroMQ的服务端通过bind绑定地址创建Socket,客户端通过connect地址建立连 接。服务端与客户端的消息类型必须匹配。下面介绍ZeroMQ支持的有效消息类型 配对组合。

再次提醒,在下面的组合中,服务端/客户端可以使用两种类型中的任何一种。

广播/订阅组合

一条消息同时被所有订阅者获取:

  • PUB - SUB

请求应答组合

一个请求对应一个应答,注意可以有多个请求/应答者。一个请求会随机发送给一个接收者:

  • REQ - REP

在请求-应答模式中,通常使用请求-收到应答-请求-收到应答-...这样循环的方式通信,某一方出现异常时,有可能造成阻塞。

可作中间件(broker)的组合

这些组合通常用来构件更为复杂的通信模式

  • REQ - ROUTER
  • DEALER - REP
  • DEALER - ROUTER
  • DEALER - DEALER
  • ROUTER - ROUTER

DEALER,ROUTER会在收到的消息中加入连接ID,用于识别连接。

DEALER-ROUTER 可用于异步请求-应答场景,替代REQ - REP组合,避免出现阻塞。

XPUB/XSUB:原始消息转发

用于代理转发消息:

  • XPUB - XSUB

推送模式

与PUB/SUB类似都只支持单向消息,不同的是一条消息PUSH/PULL只对应一个接收者:

  • PUSH - PULL

一对一通信组合

PAIR主要用于同一应用内线程间数据交换:

  • PAIR - PAIR

使用 multicast 自定义协议

由于 ZeroMQ仅使用定长数据包,自身并不带复杂的通信协议。除了可使用自定义二进制协议外,我们还可以 利用ZeroMQ提供的multicast定义协议。

Multicast的使用很简单,在发送数据包时使用SNDMORE的flag参数即表示当前 数据包为multicast包之一,

socket.send(&msg, SNDMORE)

将flag参数设为0表示multicast包结束:

socket.send(&msg, 0)

Multicast的包会分为多个ZeroMQ数据包传输,但ZeroMQ保证

  • 发送端在发送最后一个multicast包时才会真正发送数据包
  • 原子性:接收端要么收到所有multicast包,要么完全收不到

可见multicast适合自定义协议,不适合文件分割传输。

ZeroMQ的一些使用场景

作为代理(Broker)使用

使用proxy(frontend, backend)函数将两个ZeroMQ的Socket组合为消息代理,可实现与其它常见的消息服务器相同的功能。

下面介绍几种可能的代理组合。

多对多广播:XPUB/XSUB

XPUB/XSUB转发收到的原始消息包,通常用作代理层。

虽然不使用XPUB/XSUB节点也可以实现多对多广播,但这要求客户端预先知道服 务端的地址,这样的实现就缺少灵活性(例如服务端在内网或服务端地址为动态 变化的情况下,客户端就不好处理)。

多对多

多对多请求应答:ROUTER/DEALER

在存在多个任务分发者及多个任务处理者时,可使用此模式:

任务分发

ZeroMQ的线程安全性

  • ZeroMQ的context对象,即zmq.Context()具有线程安全性,可在多个线程中使用。
  • 但使用context创建的socket不具线程安全性,不能在多线程中使用。

在多线程场景中,正确的做法是在主线程中创建context对象,并传递给子线程 中生成socket实例,并确保一个socket仅在这个线程中使用。

使用ZeroMQ传输文件

可使用 DEALER-ROUTER 模式分片发送文件。可参考imagezmq: 一个使用ZeroMQ实现的视频传输Python库。

问题

ZeroMQ似乎没有简单的权限管理方法,也没有好用的通信加密机制。

参考

留言