ZeroMQ 介绍

93 阅读3分钟

ZeroMQ 介绍

  • ZeroMQ(也称为ØMQ)是一个高性能的异步消息库,用于构建分布式和并发应用。它提供了多种通信模式,如请求-回复、发布-订阅、推-拉等,使得进程间通信,多机通信等变得简单。

  • ​套接字类型​​:ZeroMQ提供了多种套接字类型,每种类型对应一种通信模式:

    • ​请求-回复(REQ-REP)​​:用于同步的请求-回复模式,类似HTTP。
    • ​发布-订阅(PUB-SUB)​​:用于一对多的消息分发,发布者发送消息,订阅者接收感兴趣的消息。
    • ​​推-拉(PUSH-PULL)​​:用于管道模式,消息从推端流向拉端,适合负载均衡。
    • ​配对(PAIR)​​:用于一对一的通信,两个套接字直接连接。
    • ​路由-委托(ROUTER-DEALER)​​:用于高级的请求-回复模式,可以构建复杂的代理结构。

传输协议

ZeroMQ 支持多种传输协议,每种协议针对不同的使用场景,不同平台(windows,linux,vxworks,..)支持也有差异,protocol定义如下:

/// src/address.hpp
namespace protocol_name
{
static const char inproc[] = "inproc";
static const char tcp[] = "tcp";
static const char udp[] = "udp";
#ifdef ZMQ_HAVE_OPENPGM
static const char pgm[] = "pgm";
static const char epgm[] = "epgm";
#endif
#ifdef ZMQ_HAVE_NORM
static const char norm[] = "norm";
#endif
#ifdef ZMQ_HAVE_WS
static const char ws[] = "ws";
#endif
#ifdef ZMQ_HAVE_WSS
static const char wss[] = "wss";
#endif
#if defined ZMQ_HAVE_IPC
static const char ipc[] = "ipc";
#endif
#if defined ZMQ_HAVE_TIPC
static const char tipc[] = "tipc";
#endif
#if defined ZMQ_HAVE_VMCI
static const char vmci[] = "vmci";
#endif
}
  • 简单介绍下linux上常用的protocol:
    • 其中 udp 更适合可靠性要求不高、实时性要求高的广播模式。
    • inproc 作用不大,线程间通过BlockingQueue等队列传数据对象共享指针更好,对于复杂结构体数据会省去序列化/反序列化操作,性能更好。
协议前缀适用场景性能特点
tcptcp://跨网络通信高吞吐量,可靠
ipcipc://进程间通信低延迟,高性能
udpudp://多播/广播实时性,可扩展
inprocinproc://线程间通信最快

套接字类型与协议常用组合:

套接字类型适用协议典型应用场景
PUB/SUBTCP,UDP, PGM, EPGM, IPC发布/订阅模式,一对多消息分发
XPUB/XSUBTCP, IPC代理模式,消息过滤和转发
PUSH/PULLTCP, IPC管道模式,负载均衡
REQ/REPTCP, IPC请求/响应模式
ROUTER/DEALERTCP, IPC高级路由模式,负载均衡器
PAIRTCP, IPC点对点通信

整体类图框架

zeromq_class.svg

  • 整体的结构还是比较清晰的,主要是 socket_base_t 和其子类实现(pub_t,sub_t,push_t,pull_t),其他的类都是为此服务的。

使用示例

simple_server.c 启动序列图

simple_server_sequence.png

  • 在首次创建 socket 时会创建 线程 reaper , io_threads

    • reaper 主要通过 zmq::epoll_t 开启loop线程,等待事件并通过回调 in_event () 来异步处理 _mailbox 中 cmd 指令数据;
    • io_threads 默认数量为 1 ,可在启动前通过参数修改,也是通过 zmq::epoll_t 开启loop线程,等待IO事件(EPOLLERR,EPOLLHUP,EPOLLIN,EPOLLOUT)并 调用事件对象的 in_event()/out_event() 处理;
  • recv/send 默认都是阻塞的,实际使用肯定不止一个socket,通过poller监听多个socket 的 IN/OUT 事件再去做对应处理更合适;

  • 一些参数 keep_alive , timeout , buff_size 要根据实际socket type 和 实际场景来设置;

  • 实际使用可以使用zeromq 的 epoll 来同时处理多个socket 消息事件,相比于每个socket 开一个线程处理效率更高。

  • zeromq 消息传输不一定完全可靠,消息内容最好加上 发送者身份标记字段,消息id字段(自增id,UUID)等进行处理。