假设我有一个 PUB
服务器 zmq_send()
的实时消息 SUB
< / strong>客户。如果客户端很忙且无法足够快地 zmq_recv()
消息,那么消息将在客户端(和/或服务器)中缓冲。
如果缓冲区变得太大(高水位线),则会丢弃新消息。对于实时消息,这与人们想要的相反。应该删除旧消息以便为新消息添加。
有没有办法做到这一点?
理想情况下,我希望SUB
客户端的接收队列为空或仅包含最新消息。当收到新消息时,它将替换旧消息。 (我想这里的问题是当队列为空时客户端会阻塞zmq_recv()
,浪费时间这样做。)
那么实时Feed通常如何在ZeroMQ
中实现?
答案 0 :(得分:5)
我会在这里回答我自己的问题。设置ZMQ_CONFLATE&#34;仅保留最后一条消息&#34;似乎很有希望,但它不适用于订阅过滤器。它只会在队列中保留一条消息。如果您有多个过滤器,则其他过滤器类型的旧消息和新消息都将被丢弃。
同样,zeromq指南的建议只是为了杀死慢速订阅者,但这似乎不是现实的解决方案。拥有不同阅读速度的订阅者,订阅相同的快速发布者,应该是正常的用例。其中一些订阅者可能生活在速度较慢的计算机上,其他订阅者也可能生活在快速计算机等等.ZeroMQ应该能够以某种方式处理它。
http://zguide.zeromq.org/page:all#Slow-Subscriber-Detection-Suicidal-Snail-Pattern
我最终在客户端手动删除旧的排队消息。它似乎工作正常。我通过那种方式获得订阅的消息,这些消息的时间小于3毫秒(通过tcp localhost)。即使在我有五千条10秒旧消息的情况下,这也适用于在后面那些少量实时消息前面的队列中。这对我来说已经足够了。
我无法帮助,但认为这应该是图书馆提供的。它可能会做得更好。
无论如何这里是客户端,旧消息丢失,代码:
bool Empty(zmq::socket_t& socket) {
bool ret = true;
zmq::pollitem_t poll_item = { socket, 0, ZMQ_POLLIN, 0 };
zmq::poll(&poll_item, 1, 0); //0 = no wait
if (poll_item.revents & ZMQ_POLLIN) {
ret = false;
}
return ret;
}
std::vector<std::string> GetRealtimeSubscribedMessageVec(zmq::socket_t& socket_sub, int timeout_ms)
{
std::vector<std::string> ret;
struct MessageTmp {
int id_ = 0;
std::string data_;
boost::posix_time::ptime timestamp_;
};
std::map<int, MessageTmp> msg_map;
int read_msg_count = 0;
int time_in_loop = 0;
auto start_of_loop = boost::posix_time::microsec_clock::universal_time();
do {
read_msg_count++;
//msg format sent by publisher is: filter, timestamp, data
MessageTmp msg;
msg.id_ = boost::lexical_cast<int>(s_recv(socket_sub));
msg.timestamp_ = boost::posix_time::time_from_string(s_recv(socket_sub));
msg.data_ = s_recv(socket_sub);
msg_map[msg.id_] = msg;
auto now = boost::posix_time::microsec_clock::universal_time();
time_in_loop = (now - start_of_loop).total_milliseconds();
if (time_in_loop > timeout_ms) {
std::cerr << "Timeout reached. Publisher is probably sending messages quicker than we can drop them." << std::endl;
break;
}
} while ((Empty(socket_sub) == false));
if (read_msg_count > 1) {
std::cout << "num of old queued up messages dropped: " << (read_msg_count - 1) << std::endl;
}
for (const auto &pair: msg_map) {
const auto& msg_tmp = pair.second;
auto now = boost::posix_time::microsec_clock::universal_time();
auto message_age_ms = (now - msg_tmp.timestamp_).total_milliseconds();
if (message_age_ms > timeout_ms) {
std::cerr << "[SUB] Newest message too old. f:" << msg_tmp.id_ << ", age: " << message_age_ms << "ms, s:" << msg_tmp.data_.size() << std::endl;
}
else {
std::cout << "[SUB] f:" << msg_tmp.id_ << ", age: " << message_age_ms << "ms, s:" << msg_tmp.data_.size() << std::endl;
ret.push_back(msg_tmp.data_);
}
}
return ret;
}