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等队列传数据对象共享指针更好,对于复杂结构体数据会省去序列化/反序列化操作,性能更好。
| 协议 | 前缀 | 适用场景 | 性能特点 |
|---|---|---|---|
| tcp | tcp:// | 跨网络通信 | 高吞吐量,可靠 |
| ipc | ipc:// | 进程间通信 | 低延迟,高性能 |
| udp | udp:// | 多播/广播 | 实时性,可扩展 |
| inproc | inproc:// | 线程间通信 | 最快 |
套接字类型与协议常用组合:
| 套接字类型 | 适用协议 | 典型应用场景 |
|---|---|---|
| PUB/SUB | TCP,UDP, PGM, EPGM, IPC | 发布/订阅模式,一对多消息分发 |
| XPUB/XSUB | TCP, IPC | 代理模式,消息过滤和转发 |
| PUSH/PULL | TCP, IPC | 管道模式,负载均衡 |
| REQ/REP | TCP, IPC | 请求/响应模式 |
| ROUTER/DEALER | TCP, IPC | 高级路由模式,负载均衡器 |
| PAIR | TCP, IPC | 点对点通信 |
整体类图框架
- 整体的结构还是比较清晰的,主要是 socket_base_t 和其子类实现(pub_t,sub_t,push_t,pull_t),其他的类都是为此服务的。
使用示例
simple_server.c 启动序列图
-
在首次创建 socket 时会创建 线程
reaper,io_threadsreaper主要通过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)等进行处理。