ZeroMQ同时发布和订阅

时间:2016-05-17 01:09:46

标签: c++ zeromq publish-subscribe distributed-system low-latency

我正在开发一个C ++程序,该程序需要能够从任意数量的其他客户端发送/接收 JSON -payloads。

起初,我尝试实现PubNub服务,但我认为我无法同时获取和发布消息(即使在不同的线程上使用两个不同的上下文)。我需要能够做到这一点。我还发现PubNub对我的喜好有太多的延迟。

我遇到了ZeroMQ库,其中包含符合我需要的 PUB/SUB 模型。但是我遇到的所有examples都解释了如何以一个进程是发布者或订阅者的方式实现它,而不是两者同时

理想情况下,我想编写一个服务器,将所有来自任何人的消息转发给订阅消息中指定的特定频道的任何人。 任何人都应该能够接收并向网络上的任何其他人发布消息,前提是他们订阅了正确的频道。

UPDATE 1:

注意:我不需要收据保险,因为有效负载N + 1优先于有效负载N.我想要发送并忘记通信的意思(类似于UDP)。

根据要求: PubNub 32 kBJSON -payload的限制对我来说非常完美,我不需要更多。事实上,我的有效负载平均在 4 kB 附近。所有客户端实例都将在同一本地网络上运行,因此延迟应该理想地小于 5 ms 。至于客户数量,一次不超过4个客户订阅同一频道/主题。

UPDATE 2 :

我无法预测将提前有多少频道/主题存在,但它将在数十个(大部分时间),数百个(达到峰值)的数量级。不是数千人。

问题:

Q1: - 我可以使用ZeroMQ实现此类行为吗? Q2: - 是否有任何工作样本证明(最好在C++)? Q3: - 如果没有,请C++中有关于图书馆的任何建议吗?

pubsub architecture

3 个答案:

答案 0 :(得分:1)

  

ZeroMQ : 能够在上面给出的量表中很好地完成此任务    nanomsg : 也可以提供此任务,需要交叉检查客户端的端口/绑定

     

设计评审:

  • 客户端实例不是持久性的,可能会自行出现,可能会自行消失或出错
  • client 实例决定它自己,它是什么PUB - 作为消息有效负载lish
  • client 实例决定它自己,它是什么SUB - 作为实际传入的消息流TOPIC划线 - 过滤器
  • 客户端实例交换(发送),它自己,它准备/生成的简单,非多部分,JSON格式的消息
    < / LI>
  • client 实例收集(接收)消息,假定它们处于相同的,非多部分,JSON格式的形状,并且尝试获取该消息。本地处理的em将在收到完成后发生
  • client -instances的最大数量不超过数百个
  • 任何JSON格式有效负载的最大大小小于 32 kB ,平均约为4 kB
  • 通过常见LAN冲突域的E2E流程到流程交付可接受的最大延迟时间<<> 5,000 [usec]
  • server instance是一个中心角色和一个持久性实体
  • server 实例为所有迟加盟者提供了一个已知的传输类URL - 目标&#39; .connect() - s
  

<强>提案:
  
   服务器 可以使用PUBSUB行为部署多个行为以实现既定目标,并提供代码驱动,快速, SUB - 附加的非阻塞事件循环.poll(),其中任何一个 SUB -side {{1}的对齐重新传输有效负载到它的 .recv() -side,当前为PUB - ed,受众群体(直播客户端实例):<登记/>
  设置.connect()
  和s_SUB_recv = aZmqCONTEXT.socket( zmq.SUB );

  出于性能原因,这里并不是那么困难,人们也可能将工作负载流分离出来。通过在多个创建的I / O线程的分离子集上映射每个来进行处理:

  地图s_PUB_send = aZmqCONTEXT.socket( zmq.PUB );
  和s_SUB_recv.setsockopt( ZMQ_AFFINITY, 0 );
  
  设置s_PUB_send.setsockopt( ZMQ_AFFINITY, 1 );
+
  设置s_PUB_send.bind( "tcp://localhost:8899" );
  设置s_SUB_recv.setsockopt( ZMQ_SUBSCRIBE, "" ); // forever *every*-TOPIC
  设置s_SUB_recv.setsockopt( ZMQ_MAXMSGSIZE, 32000 ); // protective ceiling
  设置s_SUB_recv.setsockopt( ZMQ_CONFLATE, True ); // retain just the last msg
  设置s_SUB_recv.setsockopt( ZMQ_LINGER, 0 ); // avoid blocking

  和s_SUB_recv.setsockopt( ZMQ_TOS, anAppToS_NETWORK_PRIO_CODE );

  同样,
   客户端 实例可以部署s_SUB_recv.bind( "tcp://localhost:8888" ); // [PUB]s .connect() - 端点和PUB - 端点的反向串联,准备好SUB到已知的运输目标 - .connect()

  本地客户端特定订阅决定从传入的消息流中过滤什么(在URL之前,所有消息的丰富度将传递到每个客户端传输类上的实例,但是自 ZeroMQ v.3.1 API 以来,主题过滤器正在API v.3.1+侧运行,在所需的模式操作中,它消除了浪费的卷通过网络的数据,但同时,这增加了PUB侧处理开销(参考:备注的原则,增加了多I / O线程映射/上面的表现提升)

  设置PUB
  和c_SUB_recv = aZmqCONTEXT.socket( zmq.SUB );

  除非有效负载 - 组装/处理开销增长接近允许的端到端延迟阈值,否则不需要在此处分离/隔离c_PUB_send = aZmqCONTEXT.socket( zmq.PUB );低级I / O线程:
  地图ZeroMQ
  和c_SUB_recv.setsockopt( ZMQ_AFFINITY, 0 );
  
  设置c_PUB_send.setsockopt( ZMQ_AFFINITY, 1 );
+
  设置c_PUB_send.connect( "tcp://server:8888" ); // reverse .bind on [SUB]
  设置c_SUB_recv.setsockopt( ZMQ_SUBSCRIBE, "" ); // modified on-the-fly
  设置c_SUB_recv.setsockopt( ZMQ_MAXMSGSIZE, 32000 ); // protective ceiling
  设置c_SUB_recv.setsockopt( ZMQ_CONFLATE, True ); // take just last
  设置c_SUB_recv.setsockopt( ZMQ_LINGER, 0 ); // avoid blocking
  和c_SUB_recv.setsockopt( ZMQ_TOS, anAppToS_NETWORK_PRIO_CODE );


讨论:

对于业余爱好项目,消息传递基础架构上没有太多需要,但对于更严重的域, server client 实例应该有其他服务。进一步增加了正式沟通模式行为   - c_SUB_recv.connect( "tcp://server:8899" ); 用于远程键盘,具有r/KBD个类似的临时检查实用程序
  - CLI 转发器,用于允许系统范围的状态/性能监控
  - KEEP_ALIVE 处理程序,用于允许系统范围/特定于实例的SIG_EXIT   - SIG_EXIT 服务,允许安全地收集/存储日志记录的非阻塞副本(无论是在调试阶段还是性能 - 调整阶段或生产级证据记录中)收集)

  - distributed syslog 审计跟踪工具等   - Identity Management 用于增加基础架构的稳健性,使其更好地免受DoS攻击/中毒错误的NIC流量突发等问题
  - WhiteList/BlackList 用于智能/ ad-hoc基础架构设计和状态监控,或者当多角色/(N + M)阴影的主动热备用角色时 - 移交/接管场景等进入舞台

摘要

Adaptive Node re-Discovery:是的,完全在ZeroMQ能力范围内 A1:是的,ZeroMQ书中的C ++代码示例/指南可用 A2:参考:A1,加上可能会深入评论in Martin SUSTRIK's post on "Differences between nanomsg and ZeroMQ"

希望您能享受分布式处理的强大功能,并得到A3ZeroMQ或两者的支持。

只有自己的想象力才是极限。

如果对further details, one might love the book referred to in the The Best Next Step section of this post感兴趣

答案 1 :(得分:0)

  

Q1: - 我可以使用ZeroMQ实现此类行为吗?

肯定是的;但可能没有使用PUB/SUB套接字。

使用PUB/SUB执行此操作的方法是:对于系统中的每个节点,您创建一个PUB套接字和一个SUB套接字,并连接单个SUB } socket到所有其他节点的PUB个套接字,并相应地设置您的订阅过滤器。这是有用的,因为(我认为)您需要为所有连接设置相同的过滤器。请注意,您绝对应该 NOT 在每个节点中创建多个上下文。

如果您的节点总数很少(例如10-20或更少),您可以为每个节点创建一个PUB套接字和N-1个SUB套接字(仍然在一个上下文中)并将每个SUB套接字连接到其他节点的每个PUB套接字。

如果您有明确的客户端和服务器节点概念,我可以使用较新的CLIENT/SERVER套接字(4.24.1中提供,我相信。)这将更加优雅,可能更容易管理,但您必须自己实施内容过滤(&#34;频道&#34;);根据你想要做什么,这可能很容易或有点牵连。

  

Q2: - 是否有任何工作样本证明(最好是在C ++中)?

不是我知道的。

  

Q3: - 如果没有,有关C ++库的任何建议吗?

我仍然建议ZeroMQ,因为它相对轻巧,简洁优雅的界面,全面的功能,以及使用多种语言工作的能力。有很多插座组合可供选择。如果情况变得更糟,您可以随时随地使用PAIR个套接字。

答案 2 :(得分:0)

使用 nanomsg 协议

BUS ,请参阅http://nanomsg.org/documentation-zeromq.html