ZeroMQ官方文档第1章 – 基础知识
0 准备工作
在Linux中安装libzmq和czmq:参考链接
sudo apt update
sudo apt install -y
git build-essential libtool
pkg-config autotools-dev autoconf automake cmake
uuid-dev libpcre3-dev valgrind
git clone https://github.com/zeromq/libzmq.git
cd libzmq
./autogen.sh
./configure
make check
sudo make install
sudo ldconfig
cd ..
git clone https://github.com/zeromq/czmq.git
cd czmq
./autogen.sh && ./configure && make check
sudo make install
sudo ldconfig
cd ..
将zguide下载下来:
git clone --depth=1 https://github.com/imatix/zguide.git
之后如果写程序,可在程序中包含czmq.h,以如下命令编译链接:
gcc myapp.c -o myapp -lczmq -lzmq
1 Ask and Ye Shall Receive(基础例程)
本小节分析hwserver和hwclient程序(hw表示helloworld):
cd zguide/examples/C
./build hwserver
./build hwclient
开2个终端分别运行./hwserver
和./hwclient
以测试。原理如下图所示:
Server和Client在send()之后转入recv()状态,等待接收对方发来的信息。
2 A Minor Note on Strings(C字符串处理)
下面给出函数s_recv,将socket中接收的ZMQ字符串转为C字符串(无接收时返回NULL,字符串过长时截断,添加尾0):
static char *
s_recv (void *socket) {
char buffer [256];
int size = zmq_recv (socket, buffer, 255, 0);
if (size == -1)
return NULL;
if (size > 255)
size = 255;
buffer [size] = '';
/* use strndup(buffer, sizeof(buffer)-1) in *nix */
return strdup (buffer);
}
同理可以有s_send函数,这些都在zhelpers.h中(还有s_sendmore函数等),C语言开发者可以直接调用,更为方便。
3 Getting the Message Out(发布-订阅模式)
发布-订阅模式是异步的,可能会有消息漏接收的情况(第2章有方法解决)。即使启动了订阅者,稍等片刻,然后启动发布者,订阅者也始终会错过发布者发送的第一条消息。这是因为当订阅者连接到发布者时(这需要一小段时间,但非零时间),发布者可能已经在发送消息。示例代码在wuserver.c和wuclient.c中。
关于发布-订阅(pub-sub)模式的一些要点:
一个订阅者可以连接多个发布者,每次使用一个连接调用。然后,数据将到达并交错排列(”公平排队”),这样就不会因为一个发布者而淹没其他发布者。
如果一个发布者没有连接的订阅者,那么它就会直接丢弃所有信息。
如果使用的是 TCP,而订阅者的速度很慢,那么信息就会在发布者上排队。我们稍后将讨论如何使用 “高水位线 “来保护发布者。
从 ZeroMQ v3.x 开始,当使用连接协议(tcp:@<>@ 或 ipc:@<>@)时,过滤发生在发布者端。
4 Divide and Conquer (PUSH-PULL模式)
该流水线模式也存在”慢速加入者”综合征,即当其中1个PULL socket的加入速度比其他socket更快时,会比其他工作线程获得更多的来自上游消息,导致不能正确负载均衡(在第3章中有方法解决)。
Programming with ZeroMQ
Getting the Context Right
上下文是单个进程中所有套接字的容器。在进程开始时调用 zmq_ctx_new() 一次,在进程结束时调用 zmq_ctx_destroy() 一次。
Making a Clean Exit
如果可以,请使用 zmq_send()
和 zmq_recv()
,因为这样可以避免使用 zmq_msg_t
对象。
如果确实使用了 zmq_msg_recv()
,请务必在使用完毕后立即调用 zmq_msg_close()
,以释放接收到的消息。
如果你正在打开和关闭大量的套接字,这可能表明需要重新设计你的应用程序。在某些情况下,套接字句柄在您销毁上下文之前不会被释放。
退出程序时,关闭套接字,然后调用 zmq_ctx_destroy()
。这将销毁上下文。
此外,还有一些多线程下的clean事项,下一章将详细介绍。
Why We Needed ZeroMQ
TCP消息传递面临的典型问题:
我们如何处理 I/O?是阻塞应用程序,还是在后台处理 I/O?这是一个关键的设计决策。阻塞 I/O 会导致架构不能很好地扩展。但后台 I/O 也很难做得很好。
我们如何处理动态组件,即暂时消失的组件?我们是否要正式将组件分为 “客户端 “和 “服务器”,并规定服务器不能消失?如果我们要将服务器连接到服务器,又该怎么办?我们是否每隔几秒钟就尝试重新连接一次?
我们如何在线路上表示信息?我们如何构建数据框架,使其易于写入和读取、避免缓冲区溢出、高效处理小型信息,同时又足以处理最大的戴着派对帽的跳舞猫视频?
我们如何处理无法立即发送的信息?尤其是在等待某个组件重新上线的情况下?我们是丢弃消息、将其存入数据库还是存入内存队列?
消息队列存放在哪里?如果从队列中读取信息的组件非常慢,导致队列堆积,该怎么办?我们的策略是什么?
我们如何处理丢失的消息?是等待新数据、请求重新发送,还是建立某种可靠性层来确保消息不会丢失?如果可靠性层本身崩溃了怎么办?
如果我们需要使用不同的网络传输方式怎么办?比如,用组播代替 TCP 单播?或者 IPv6?我们需要重写应用程序,还是在某个层中抽象传输?
我们如何路由信息?我们能否将同一信息发送给多个对等体?能否将回复发送回原始请求者?
如何为另一种语言编写应用程序接口?是重新实现一个线级协议,还是重新打包一个库?如果是前者,我们如何保证高效稳定的堆栈?如果是后者,我们如何保证互操作性?
我们如何表示数据以便在不同架构之间读取?我们要为数据类型强制执行特定的编码吗?这在多大程度上是消息传递系统而不是更高层的工作?
我们如何处理网络错误?是等待并重试,还是默默忽略,抑或终止?
我们需要的是一种能完成消息传递工作的软件,但它必须简单、廉价,能在任何应用程序中使用,成本几乎为零。它应该是一个只需链接即可使用的库,没有任何其他依赖性。没有额外的移动部件,因此没有额外的风险。它应能在任何操作系统上运行,并能与任何编程语言配合使用。
这就是 ZeroMQ:一个高效的、可嵌入的库,它能解决应用程序所需的大部分问题,使其在网络上具有很好的弹性,而无需太多成本。具体来说:
它在后台线程中异步处理 I/O。这些线程使用无锁数据结构与应用程序线程通信,因此并发的 ZeroMQ 应用程序不需要锁、信号灯或其他等待状态。
组件可以动态来去,ZeroMQ 会自动重新连接。这意味着您可以按任何顺序启动组件。您可以创建 “面向服务的架构”(SOA),其中的服务可以随时加入或离开网络。
它能在需要时自动将信息排成队列。它能智能地完成这项工作,在排队之前尽可能将信息推送到接收方附近。
它有办法处理过满的队列(称为 “高水位线”)。当队列已满时,ZeroMQ 会自动阻止发送者或丢弃消息,具体取决于你正在进行的消息传递类型(即所谓的 “模式”)。
ZeroMQ 允许应用程序通过任意传输方式相互通信: TCP、组播、进程内、进程间。您无需修改代码即可使用不同的传输方式。
它能根据消息传递模式使用不同的策略,安全地处理缓慢/阻塞的读取器。
它允许你使用各种模式(如请求-回复和发布-从属)路由信息。这些模式是创建网络拓扑结构的方法。
您只需一次调用,就能创建代理来排队、转发或捕获信息。代理可以降低网络互连的复杂性。
它通过在线路上进行简单的构架,将整个报文原封不动地发送出去。如果你写了一条 10k 报文,你将收到一条 10k 报文。
它不对报文施加任何格式。它们是大小从零到千兆字节不等的 blob。当你想表示数据时,你可以在上面选择其他产品,如 msgpack、谷歌的协议缓冲区等。
它能智能处理网络错误,在合理的情况下自动重试。
它能降低功耗。用更少的 CPU 做更多的事情,这意味着服务器耗电更少,而且你可以让旧服务器使用更长时间。
备注
hwserver.c //ZMQ_REP模式
hwserver2.c //ZMQ_REP模式
hwserver3.c //ZMQ_ROUTER模式,3段消息(identity+空+数据)帧
hwserver4.c //ZMQ_ROUTER模式
hwclient.c //ZMQ_REQ模式
hwclient2.c //ZMQ_DEALER模式,2段消息(空+数据)
hwclient3.c //ZMQ_REQ模式
hwclient4.c //ZMQ_DEALER模式,2段消息(空+数据)