【发布时间】:2013-04-20 03:47:56
【问题描述】:
我对 ZeroMQ PUB/SUB 进行了快速测试,现在有了一些工作代码。但是,我对 zeromq 中应用的高水位线的概念有点困惑。
我在发布者代码中设置了一个 HWM,它为连接到套接字的每个订阅者设置队列长度。
然而,也可以在订阅者的接收套接字上设置一个 HWM。是否有任何理由在订阅者端设置 HWM,这与设置发布者 HWM 有何不同?
【问题讨论】:
我对 ZeroMQ PUB/SUB 进行了快速测试,现在有了一些工作代码。但是,我对 zeromq 中应用的高水位线的概念有点困惑。
我在发布者代码中设置了一个 HWM,它为连接到套接字的每个订阅者设置队列长度。
然而,也可以在订阅者的接收套接字上设置一个 HWM。是否有任何理由在订阅者端设置 HWM,这与设置发布者 HWM 有何不同?
【问题讨论】:
在发布者中,我们几乎总是应该仔细考虑 HWM,因为有很多原因会导致崩溃(内存不足)影响整个系统(因为发布者为所有订阅者提供服务)。
同样在订阅者中,在某些情况下调节 HWM 可能是有用的,但这主要取决于订阅者的性质、它如何处理收到的消息以及大量接收到的消息无法及时处理的概率;以及预期的运行时环境(可用内存量、订阅者数量等)
ZMQ 使用 HWM(高水位线)的概念来定义其内部管道的容量。每个从套接字出来或进入套接字的连接都有自己的管道,以及用于发送和/或接收的 HWM,具体取决于套接字类型。一些套接字(PUB、PUSH)只有发送缓冲区。有些(SUB、PULL、REQ、REP)只有接收缓冲区。有些(DEALER、ROUTER、PAIR)同时具有发送和接收缓冲区。
可用的socket option 是:
ZMQ_SNDHWM:为出站邮件设置高水位线(...在publisher socket 上)ZMQ_RCVHWM:为入站邮件设置高水位线(...在subscriber socket 上)ZMQ 3.0+ 强制对其内部缓冲区(所谓的 HWM)进行默认限制,因为 HWM 是减少内存溢出问题的好方法
ZMQ_PUB 和 ZMQ_SUB 都将 ZMQ_HWM 选项操作设置为“Drop”,因此当限制丰富时,订阅者的内存或发布者应该停止增长,至少取决于 ZMQ 缓冲区.
通常最需要保护以防止不加区别地使用内存(内存不足问题)的是发布者:
在inproc传输中,发送方和接收方共享相同的缓冲区,所以真正的HWM是双方设置的HWM之和。
但是如果你使用 TCP 并且订阅者很慢,消息会在发布者上排队。
PUB-SUB 的常见故障原因包括:
在发布者上排队消息会使发布者耗尽内存并崩溃,尤其是在有很多订阅者并且出于性能原因无法刷新到磁盘的情况下。
从发布者的角度来看,我们通过正确设置 ZWM 使用的伟大策略是一段时间后停止排队新消息,新消息只会被拒绝或掉了;这是 ØMQ 在发布者设置 HWM 时所做的。
ZMQ 还可以在订阅者上排队消息
如果有人要耗尽内存并崩溃,那将是订阅者而不是发布者,这是公平的。这对于订阅者暂时无法跟上,但在流变慢时可以赶上的“高峰”流来说是完美的。
注意:HWM 不准确;虽然默认情况下您可能会收到多达 1,000 条消息,但由于 libzmq 实现其队列的方式,实际缓冲区大小可能要小得多(只有一半)。
这些假设的主要来源是 Pieter Hintjens 的书“Code Connected Volume 1”,以电子格式在线提供;它有一章专门介绍 High-Water Marks,其中包含有关该主题的进一步解释。
【讨论】:
如果您一次订阅超过 1000 个主题,则在订阅者上设置 ZMQ_SNDHWM 会很有用(据我所知,默认情况下 ZMQ_SNDHWM 为 1000)。所以如果你这样订阅:
for(i = 0; i < 2000; i++)
{
subscriber.setsockopt( ZMQ_SUBSCRIBE, &i, sizeof(i));
}
由于输出队列溢出,它将无法工作。
但是如果你在连接之前设置了
subscriber.setsockopt( ZMQ_RCVHWM, &i, sizeof(i));
subscriber.setsockopt( ZMQ_SNDHWM, &i, sizeof(i));
效果很好。
【讨论】: