从ZeroMQ GitHub issues page开始交叉发布,以提高可见度。
注意:这可能是预期的行为。
我正在实现一种PUB / SUB模式,其中有一个发布者(相当快)和多个订阅者(它们具有不同的费率)。
我对最新消息感兴趣。因此,我想启用ZMQ_CONFLATE
选项。
我的理解(只是从文档中,我没有检查库的源代码)是仅保留最新消息-大小为1的队列以及最新消息。我们无法使用高水位标记来丢弃新消息。
我假设“用户”代码发送一条消息(zmq_send
),zmq_context
中的线程池将处理要在套接字上发送的消息。如果在发送旧消息之前出现新消息,则(ZMQ_CONFLATE
= true)将丢弃旧消息,然后发送新消息。接收器大小相同。如果用户代码调用zmq_recv
的速度很慢,则仅读取最后一条消息。
现在,只要我有1个发布者和1个订阅者,它就可以完美地工作。
如果我添加更多的订户,则只有第一个连接的订户(使用tcp
或最后一个(使用ipc
)获得消息。
请注意,由发布者方控制行为。在订户中添加ZMQ_CONFLATE
无效(在某种意义上,订户可以连接)
所以,我的问题是:这是预期的行为吗?
在发布者侧设置的ZMQ_CONFLATE
是否在将消息发送给第一个订户之后丢弃该消息(不是因为已经调用了新的zmq_send
)?
我在macOS 13.6,lib zmq(由自制软件安装)4.2.5上对此进行了测试。 我还在两个Debian(4.2.3-1和4.2.1-4)之间进行了测试
我运行的代码如下:
发布者:
#include <zmq.h>
#include <cstdlib>
#include <chrono>
#include <iostream>
#include <thread>
#include <atomic>
#include <csignal>
static std::atomic<bool> s_shutdown(false);
void signal_handler(int) {
s_shutdown = true;
}
int main(int argc, char** argv)
{
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
void* context = zmq_ctx_new();
void* output = zmq_socket(context, ZMQ_PUB);
// We set the internal queue to have only one (the most recent) message and throw away
// everything else.
int trueValue = 1;
zmq_setsockopt(output, ZMQ_CONFLATE, &trueValue, sizeof(int));
// We are ready. Create the endpoints.
// zmq_bind(output, "tcp://*:10000");
zmq_bind(output, "ipc:///tmp/zmq_test");
unsigned i = 0;
while(true) {
if (s_shutdown) {
break;
}
if (zmq_send(output, &i, sizeof(unsigned), 0) == -1) {
std::cerr << "Failed to send message." << std::endl;
} else {
std::cerr << "Sending " << i << std::endl;
}
i++;
// fake a sleep (1ms)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
zmq_close(output);
zmq_ctx_term(context);
return 0;
}
订户
#include <zmq.h>
#include <cstdlib>
#include <atomic>
#include <csignal>
#include <iostream>
#include <thread>
static std::atomic<bool> s_shutdown(false);
void signal_handler(int) {
s_shutdown = true;
}
int main(int argc, char** argv)
{
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
void* context = zmq_ctx_new();
void* input = zmq_socket(context, ZMQ_SUB);
// subscribe to everything
zmq_setsockopt(input, ZMQ_SUBSCRIBE, "", 0);
// Setting the buffer to accept a single message and to drop old messages.
int trueValue = 1;
zmq_setsockopt(input, ZMQ_CONFLATE, &trueValue, sizeof(int));
// zmq_connect(input, "tcp://localhost:10000");
zmq_connect(input, "ipc:///tmp/zmq_test");
while(true) {
// reading the state
if (s_shutdown) {
break;
}
unsigned readValue = 0;
int readBytes = zmq_recv(input, &readValue, sizeof(unsigned), ZMQ_DONTWAIT);
if (readBytes > 0) {
std::cerr << "Read " << readValue << std::endl;
}
// fake a sleep (100ms)
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
zmq_close(input);
zmq_ctx_term(context);
return 0;
}
评论发布者中的zmq_setsockopt(output, ZMQ_CONFLATE, &trueValue, sizeof(int));
允许多个订阅者接收消息。