ZeroMQ不同速度的用户看到相同的消息

时间:2018-04-26 15:40:31

标签: c++ zeromq

我在c ++中使用zmq 2.2(旧版本,我知道)来创建一个拥有多个连接订阅者的发布者,这些订阅者以不同的速度读取消息。根据我对文档的理解以及Peter Hintjens的回答here,每个订阅者都有自己的队列,并且发布者每个连接的订阅者都有一个队列。这似乎表明每个订阅者都从发布者那里接收独立于其他订阅者的消息。

但是,在下面的代码段中,快速和慢速订阅者会收到类似的消息或完全相同的消息(即使我在Point A 处增加睡眠时间并更改 { B 点{1}}

有人能说清楚为什么会发生这种情况吗?

ZMQ_HWM

编译:{{1​​}} by GCC 6.3

示例输出:

#include <zmq.hpp>
#include <unistd.h>
#include <iostream>
#include <vector>
#include <future>
using socket_t = zmq::socket_t;
using context_t = zmq::context_t;
using msg_t = zmq::message_t;
using namespace std;

vector<int> slow_consumer(int64_t hwm, int to_read)
{
    vector<int> v;
    context_t context{1};
    socket_t socket(context, ZMQ_SUB);
    socket.setsockopt(ZMQ_HWM, &hwm, sizeof(hwm));
    socket.setsockopt(ZMQ_SUBSCRIBE, "", 0);
    socket.connect("tcp://localhost:5554");
    msg_t msg;
    sleep(3);  // 3 seconds
    for (int i = 0; i < to_read; i++)
    {
        socket.recv(&msg);
        usleep(10000);  // 10 miliseconds ___________________________POINT A
        v.emplace_back(*reinterpret_cast<int*>(msg.data()));
    }
    return v;
}
vector<int> fast_consumer(int64_t hwm, int to_read)
{
    vector<int> v;
    context_t context{1};
    socket_t socket(context, ZMQ_SUB);
    socket.setsockopt(ZMQ_HWM, &hwm, sizeof(hwm));
    socket.setsockopt(ZMQ_SUBSCRIBE, "", 0);
    socket.connect("tcp://localhost:5554");
    msg_t msg;
    for (int i = 0; i < to_read; i++)
    {
        socket.recv(&msg);
        v.emplace_back(*reinterpret_cast<int*>(msg.data()));
    }
    return v;
}
void publisher(int64_t hwm)
{
    context_t context{1};
    socket_t socket(context, ZMQ_PUB);
    socket.setsockopt(ZMQ_HWM, &hwm, sizeof(hwm));
    socket.bind("tcp://*:5554");
    int count = 0;
    while (true) {
        msg_t msg(sizeof(count));
        memcpy(msg.data(), &count, sizeof(count));
        socket.send(msg);
        count++;
    }
}

int main() 
{
    int64_t hwm = 1;  // __________________________________________POINT B
    int to_read = 20;
    auto fast = async(launch::async, fast_consumer, hwm, to_read);
    auto slow = async(launch::async, slow_consumer, hwm, to_read);
    hwm = 1;  // Don't queue anything on the publisher
    thread pub(publisher, hwm);
    auto slow_v = slow.get();
    auto fast_v = fast.get();

    cout << "fast    slow" << endl;
    for (int i = 0; i < fast_v.size(); i ++)
    {
        cout << fast_v[i] << "   " << slow_v[i] << endl;
    }
    exit(0);
}

1 个答案:

答案 0 :(得分:1)

  

每个订阅者都有自己的队列

是的,确实......

这来自 PUB -side .Context() -instance的设计属性,其中发生了发送队列管理(更多)关于这一点将会稍后。)

您可以在[ ZeroMQ hierarchy in less than a five seconds ]部分阅读有关主要概念技巧的简短读物。

  

这似乎表明每个订阅者都会从发布者那里接收独立于其他订阅者的消息。

是的,确实......

各个&#34; 私人&#34; -queues之间没有互动。这里重要的是 ZMQ_HWM ,它的副作用是The&#34; Blocker&#34; -semantics。

在此设置中,简约 ZMQ_HWM 会阻止/阻止任何新条目插入 PUB -side&#34;私有&#34; -sending-Queue(大小不比 ZMQ_HWM == 1 更深),直到它被成功远程清空(由&#34;远程&#34; SUB -side Context() - 自主异步&#34;内部&#34;与运输相关的举措,在其可能(重新)加载 {{1 }} -side&#34; private&#34; -receiving-Queue(大小,再次,不比根据 SUB 更深)

换句话说, ZMQ_HWM == 1 -s&#39;有效载荷将被有效地丢弃,直到远程 PUB.send() -s将卸载&#34;阻止&#34; -payload从他们的&#34;远程&#34; - {{ 1}} - 实例的接收队列(大小,根据 *_SUB.recv() 而设计为不能存储多于一个的任何单个有效负载)。

以这种方式, Context() -er 在( 秘密地)期间解除了超过ZMQ_HWM == 1条消息阻止 )在 PUB.send() -side({{1}上接收一些 ~ 902601 的测试}})。

所有这些 20 消息都被 SUB < == to_read 旁边丢弃了/ strong>致电902581+ - 方法。

它是如何在里面实际工作的? PUB

内的简化视图

鉴于上面的模型示例, Context() 管理的队列池按.send()增长/收缩 - ed同伴出现和消失,但是ZeroMQ API v2.2同时TX和RX侧具有相同的高水位标记天花板。如上所述,Context()超过此限制的任何尝试都会被抛弃。

Context()
  

&#34; 快速订阅者上的消息排队慢订户&#34;

不,这不会发生。没有&#34;阵容&#34;,但只是持续时间的共同发生率,其中快速 - .connect()尚未使其成为20x .send() - s,之前慢(-ed) - TIME _____________________________ v [ ] v [ ] v [ ] v [ ] v PUB.setsockopt( ZMQ_HWM, 1 );] v PUB.send()-s [ | ] v : [ +-----------------QUEUE-length ( a storage depth ) is but one single message v _________________ : [ v [ ] : [Context()-managed pool-of-QUEUE(s) v [ ] : [ v [ ] : [ ___________________ v [ ] : [ [ ] v FAST_SUB.connect()---:------------>[?] [ ] v FAST_SUB.recv()-s : [?] [ ] v : : [?] [ ] v : : [?][?]<---SLOW_SUB.connect() ] v : : [?][?] SLOW_SUB.recv()-s ] v : .send(1)----->[1][1] : | 1 <-.recv()--------------------[?][1] : | : [?][1] : | : .send(2)----->[2][1] : | 2 <-.recv()--------------------[?][1] : | : [?][1] : | : .send(3)----->[3][1] : | 3 <-.recv()--------------------[?][?]------------.recv()-> 1 | : [?][?] : | : .send(4)----->[4][4] : | 4 <-.recv()--------------------[?][4] : | : [?][4] : | : .send(5)----->[5][4] : | 5 <-.recv()--------------------[?][4] : | : [?][4] : | : .send(6)----->[6][4] : | 6 <-.recv()--------------------[?][4] : | : [?][4] : | : .send(7)----->[7][4] : | 7 <-.recv()--------------------[?][4] : | : [?][4] : | : .send(8)----->[8][4] : | 8 <-.recv()--------------------[?][4] : | : [?][4] : | : .send(9)----->[9][4] : | 9 <-.recv()--------------------[?][?]------------.recv()-> 4 | : [?][?] : | : .send(A)----->[A][A] : | A <-.recv()--------------------[?][A] | : [?][A] | : .send(B)----->[B][A] | B <-.recv()--------------------[?][A] v : [ [ v : [ v : v 终于得到了阻止SUB

最初的差距&#34;只是.recv()阶段的影响,其中较慢的 SUB 不会尝试接收任何内容

sleep( 3 )

虽然 sleep( 3 ) - 代码必须尽可能快地调用SUB - s,但它的本地main(){ | | async(launch::async,fast|_fast____________| | async(launch::async,slow| .setsockopt |_slow____________| | ... | .setsockopt | .setsockopt | | ... | .connect | .setsockopt | | thread | ~~~~~~? | .connect | | |_pub___________________| ~~~~~~? | ~~~~~~? | | | .setsockopt | ~~~~~~? | ~~~~~~? | | | .bind | ~~~~~~? | ~~~~~~? | | | ~~~~~~? | ~~~~~~? | ~~~~~~? | | | ~~~~~~=RTO | ~~~~~~? | ~~~~~~? | | | .send()-s 1,2,..99| ~~~~~~? | ~~~~~~? | | | .send()-s 23456,..| ~~~~~~=RTO | ~~~~~~=RTO | | | .send()-s 25988,..| 25988 --> v[ 0]| : slow still sleep( 3 )-s before going to .recv() it's first message | | .send()-s 52522,..| 52522 --> v[ 1]| : slow still sleep( 3 )-s before going to .recv() it's first message | | .send()-s 79197,..| 79197 --> v[ 2]| : slow still sleep( 3 )-s before going to .recv() it's first message | | .send()-s 106365,..| 106365 --> v[ 3]| : slow still sleep( 3 )-s before going to .recv() it's first message | | .send()-s 132793,..| 132793 --> v[ 4]| : slow still sleep( 3 )-s before going to .recv() it's first message | | .send()-s 159236,..| 159236 --> v[ 5]| : slow still sleep( 3 )-s before going to .recv() it's first message | | .send()-s 184486,..| 184486 --> v[ 6]| : slow still sleep( 3 )-s before going to .recv() it's first message | | .send()-s 209208,..| 209208 --> v[ 7]| : slow still sleep( 3 )-s before going to .recv() it's first message | | .send()-s 234483,..| 234483 --> v[ 8]| : slow still sleep( 3 )-s before going to .recv() it's first message | | .send()-s 256122,..| 256122 --> v[ 9]| : slow still sleep( 3 )-s before going to .recv() it's first message | | .send()-s 281188,..| 281188 --> v[10]| : slow still sleep( 3 )-s before going to .recv() it's first message | | .send()-s 305855,..| 305855 --> v[11]| 305855 --> v[ 0]|// Messages on the fast subscriber starting here line up with messages on the slow subscriber | | .send()-s 454312,..| 454312 --> v[12]| 454312 --> v[ 1]| | | .send()-s 477807,..| 477807 --> v[13]| 477807 --> v[ 2]| | | .send()-s 502594,..| 502594 --> v[14]| 502594 --> v[ 3]| | | .send()-s 528551,..| 528551 --> v[15]| 528551 --> v[ 4]| | | .send()-s 554519,..| 554519 --> v[16]| 554519 --> v[ 5]| | | .send()-s 581419,..| 581419 --> v[17]| 581419 --> v[ 6]| | | .send()-s 606411,..| 606411 --> v[18]| 606411 --> v[ 7]| | | .send()-s 629298,..| 629298 --> v[19]| 629298 --> v[ 8]| | | .send()-s 651159,..| | 651159 --> v[ 9]| | | .send()-s 675031,..| return v | 675031 --> v[10]| | | .send()-s 701533,..|_________________| 701533 --> v[11]| | | .send()-s 727817,..| | 727817 --> v[12]| | | .send()-s 754154,..| | 754154 --> v[13]| | | .send()-s 778654,..| | 778654 --> v[14]| | | .send()-s 804137,..| | 804137 --> v[15]| | | .send()-s 830677,..| | 830677 --> v[16]| | | .send()-s 854959,..| | 854959 --> v[17]| | | .send()-s 878841,..| | 878841 --> v[18]| | | .send()-s 902601,..| | 902601 --> v[19]| | | .send()-s 912345,..| | | | | .send()-s 923456,..| | return v | | | .send()-s 934567,..| |_________________| | | .send()-s 945678,..| | | .send()-s 956789,..| | | .send()-s 967890,..| | | .send()-s 978901,..| | | .send()-s 989012,..| | | .send()-s 990123,..| | | .send()-s ad inf,..| - 实例却未保留只有一个这样的消息可以接受的空间,所有其他消息都被静静地丢弃,只要一个已经被占用的单独位置被占用。

每当 PUB 标记返回到零时,内部机制确实允许下一个.send()传递消息的实际内容(有效负载)downto由于 Context() - 绑定逻辑,以下HWM == 1 - s的队列存储和所有即将进行的尝试再次开始被静默删除。