ZMQ经销商 - 路由器通讯

时间:2018-03-14 23:12:16

标签: c++ zeromq python-3.6 communication asyncsocket

我目前正在开发一个项目,需要通过网络与来自分布式系统的某些实体的不同数据类型进行一些通信,而我正在使用ZMQ。

该项目的主要目标是建立一个中心节点,为可以随时连接的客户提供服务。对于连接的每个客户端,中央节点应管理两者之间的消息通信。

目前,目前所有通信都是通过TCP进行的。

客户端需要随时发送和接收消息,因此它们是ZMQ_DEALER类型套接字,中心节点为ZMQ_ROUTER

最初,目标是来自某个客户端的一条消息,此消息到达其他客户端。这意味着其他客户端可以看到所有相同的数据。

我使用Asynchronous Client/Server pattern因为我有兴趣让几个客户以协作方式相互交谈,可能有服务器代理或中间件。

我有一个ZMQ_DEALER套接字客户端连接到ZMQ_ROUTER套接字服务器

#include <zmq.hpp>
#include "zhelpers.hpp"
using namespace std;

int main(int argc, char *argv[])
{

    zmq::context_t context(1);
    zmq::socket_t client(context, ZMQ_DEALER);

    const string endpoint = "tcp://localhost:5559";

    client.setsockopt(ZMQ_IDENTITY, "PEER1", 5);
    cout << "Connecting to ZMQ Network Manager " << endpoint << "..." << endl;
    client.connect(endpoint);
    for (int request = 0; request < 10; request++)
    {

        s_sendmore(client, "");
        s_send(client, "Testing sending some data");

        std::string string = s_recv(client);

        std::cout << "Received reply " << request
                  << " [" << string << "]" << std::endl;
    }
}

在我的服务器代码上,我有一个接收和管理消息的ZMQ_ROUTER,将它绑定到一个井端口。该服务器是用Python制作的

import zmq
context = zmq.Context()
frontend = context.socket(zmq.ROUTER)
frontend.bind("tcp://*:5559")

# Initialize a poll set
poller = zmq.Poller()
poller.register(frontend, zmq.POLLIN)

print("Creating Server Network Manager Router")

while True:
    socks = dict(poller.poll())

    if socks.get(frontend) == zmq.POLLIN:
        message = frontend.recv_multipart()
        print(message)
        frontend.send_multipart(message)

在我的其他同行/客户端上,我有以下内容:

#include <zmq.hpp>
#include "zhelpers.hpp"
using namespace std;

int main (int argc, char *argv[])
{

    zmq::context_t context(1);
    zmq::socket_t peer2(context, ZMQ_DEALER);

    const string endpoint = "tcp://localhost:5559";

    peer2.setsockopt(ZMQ_IDENTITY, "PEER2", 5);
    cout << "Connecting to ZMQ Network Manager " << endpoint << "..." << endl;
    peer2.connect(endpoint);
    //s_sendmore(peer2, "");
    //s_send(peer2, "Probando");

    //std::string string = s_recv(peer2);

    //std::cout << "Received reply " << " [" << string << "]" << std::endl;

    for (int request = 0; request < 10; request++)
    {

        s_sendmore(peer2, "");
        s_send(peer2, "Probando");

        std::string string = s_recv(peer2);

        std::cout << "Received reply " << request
                  << " [" << string << "]" << std::endl;
    }

}

更新

但是每次执行某个客户端时,他们各自的消息都没有到达另一个对等客户端。 邮件到达ZMQ_ROUTER,并返回ZMQ_DEALER发件人来源。

enter image description here

这是因为在接收时身份帧之前是ROUTER,并且消息通过ROUTER发回;删除标识并使用该值将消息路由回相关的经销商according to the ZMQ_ROUTER section to the end page here

这是逻辑,我将DEALER的身份发送到ROUTERROUTER将该身份框架发送给我的DEALER消息< / p>

在第一个实例中,从我的实现开始,我需要由任何经销商发送的一些消息,这将由任何其他经销商可视化,而无论有多少经销商(一个或多个)连接到ZMQ_ROUTER。 从这个意义上说......是否需要满足其他经销商或其他经销商的身份框架?

如果我有DEALER ADEALER BDEALER C以及ROUTER

然后:

DEALER A发送消息...... 我希望来自经销商A的邮件到达DEALER BDEALER C以及其他可以加入我的会话对话的DEALERS ...

在此构思顺序中,是否有必要在DEALER B方面遇到DEALER CDEALER A的身份框架,以便此消息到达他?

如何知道我的实施中存在的每个经销商的身份框架? 这是在ROUTER方面做的? 我还没清楚这个

2 个答案:

答案 0 :(得分:3)

你可以让所有客户发送&#34;我在这里&#34;启动时的消息。然后中央服务器可以存储所有ID,c.f。工作人员和路由器之间的初始通信在这里:http://zguide.zeromq.org/page:all#A-Load-Balancing-Message-Broker。服务器会将所有收到的消息发送给所有当前已知的客户端。您应该添加一些心脏跳动以检测断开连接的客户端,c.f。 http://zguide.zeromq.org/page:all#Heartbeating

但是,ZeroMQ已经提供了这样的通信模式:PUB - SUB。本质上,每个客户端都有一个DEALER和一个SUB套接字连接到服务器ROUTERPUB套接字。服务器只是发送任何收到的 通过PUB套接字向所有客户端发送消息。如果这对于原始客户端来说是个问题,则可以在消息中包含客户端ID,以便每个客户端都可以使用自己的ID过滤掉消息。另请参阅本指南中的此示例 http://zguide.zeromq.org/page:all#Getting-an-Out-of-Band-Snapshot

另一个有趣的模式是Republishing Updates from Clients

Republishing Updates from Clients

此处PUSH - PULL用于将更新发送到服务器。如果不需要来自服务器的回复消息,这是有意义的。如果您不需要该示例中的状态请求,则可以省略ROUTER - DEALER部分。这里是一个使用Python简洁的示例实现。服务器侦听PULL套接字并通过PUB套接字发送所有内容:

import zmq

def main():
    # context and sockets
    ctx = zmq.Context()
    publisher = ctx.socket(zmq.PUB)
    publisher.bind("tcp://*:5557")
    collector = ctx.socket(zmq.PULL)
    collector.bind("tcp://*:5558")

    while True:
        message = collector.recv()
        print "I: publishing update %s" % message
        publisher.send(message)

if __name__ == '__main__':
    main()

客户端监听PUB套接字已有一段时间了。如果收到消息,则会记录该消息。如果达到超时,则会生成一个消息为1/10的消息:

import random
import time

import zmq

def main():

    # Prepare our context and subscriber
    ctx = zmq.Context()
    subscriber = ctx.socket(zmq.SUB)
    subscriber.setsockopt(zmq.SUBSCRIBE, '')
    subscriber.connect("tcp://localhost:5557")
    publisher = ctx.socket(zmq.PUSH)
    publisher.connect("tcp://localhost:5558")

    random.seed(time.time())
    while True:
        if subscriber.poll(100) & zmq.POLLIN:
            message = subscriber.recv()
            print "I: received message %s" % message
        else:
            rand = random.randint(1, 100)
            if rand < 10:
                publisher.send("%d" % rand)
                print "I: sending message %d" % rand

if __name__ == '__main__':
    main()

答案 1 :(得分:2)

(过早)获奖答案不符合定义的属性。

分布式系统需要同时运行智能 ,因为代理是分布式的,并且分析/测试/的错误分析和部署的生产问题都非常昂贵调试。

因此,复制/粘贴重复使用问题不兼容的想法不是实现前者的方式,而是后者越少。

因此,让我们首先回顾一下效率:

客户 - [A].send() -sa消息,O / P希望成为服务器端 - [S].recv() -ed和重新广播给所有其他客户端 [B,C,...],但[A] - 本身除外。

对资源最有效的方法是正确配置基础设施工具以实现这一目标,而无需重新发明轮子和/或使用脆弱且性能破坏性的脚手架代码。

所以:

客户端上的

- [*]最好使用下面描绘的原始代理概念。更复杂的设置,比如使用像Tkinter一样聪明的事件处理设施已经发展到包含在 .mainloop() 软实时系统中,效果更好,但它并不容易在不止一个方面开始设计 - 战斗,所以现在让我们更简单:

zmq_VERSION      = zmq.zmq_version_info()
anAgentsIDENTITY = whateverHashOrHumanReadableSTRING
notMINE          = anAgentsIDENTITY

if     zmq_VERSION[0] < 4:
           print "ZMQ{0:} ver < than expected, will exit".format( zmq_VERSION )
aCTX = zmq.Context( 2 )                        # if performance boosting is needed

#SUB ---------------------------------------------------------------------------
aSUB = aCTX.socket( zmq.SUB )
aSUB.setsockopt(    zmq.LINGER,          0 )   # protect your agent
aSUB.setsockopt(    zmq.MAXMSGSIZE,      m )   # protect your agent from DoS
aSUB.setsockopt(    zmq.AFFINITY,        1 )   # protect your server resources
aSUB.setsockopt(    zmq.HEARTBEAT_IVL,   ivl ) #     set server helping Heartbeats
aSUB.setsockopt(    zmq.HEARTBEAT_TTL,   ttl ) #     set server helping Heartbeats
aSUB.setsockopt(    zmq.INVERT_MATCHING, 1 )   #   avoid server sending data back
aSUB.setsockopt(    zmq.SUBSCRIBE,       notMINE )  #  NEVER .recv()-s  data back
...
#SUB PERFORMANCE & RESOURCES TWEAKING DETAILS GO WAY BEYOND THE SCOPE OF THIS POST

aSUB.connect(      "tcp://localhost:5557" )

#PUSH --------------------------------------------------------------------------
aPUSH = aCTX.socket( zmq.PUSH )
...
#PUSH PERFORMANCE & RESOURCES TWEAKING DETAILS GO WAY BEYOND THE SCOPE OF THIS POST

#main loop ---------------------------------------------------------------------
pass; notSoftFLAG = True; anAgentSignsWithIdentityPREFIX = anAgentsIDENTITY
while notSoftFLAG:

    if aReasonToSendSomethingToServer:
       aPUSH.send( anAgentSignsWithIdentityPREFIX
                 + ":::"
                 + aMsgPAYLOAD,
                   zmq.DONTWAIT
                   )                          # inspect ZMQError
       ...
       pass

    if aSUB.poll( 100 ):
       message = aSUB.recv( zmq.DONTWAIT )    #  NEVER .recv()-s own data back
       ...
       pass


    if aReasonToFlagLoopEXIT:
       notSoftFLAG = False
       ...
       pass

    if ...:
       ...
       pass

#main loop ---------------------------------------------------------------------
pass

#########
# ALWAYS:
#          better using context-aware try:/except:/finally:

aRetCODE = [ aSOCK.close() for aSOCK in ( aSUB, aPUSH, ) ]
...

aCTX.term()
#   .term()
#########

服务器可以避免所有麻烦,无需任何临时处理:

所有这些都在ZeroMQ基础设施中得到很好的调整:

pass;  zmq_VERSION = zmq.zmq_version_info()
if     zmq_VERSION[0] < 4:
           print "ZMQ{0:} ver < than expected, will exit".format( zmq_VERSION )

aCTX = zmq.Context( 2 )                        # if performance boosting is needed

#PUB ---------------------------------------------------------------------------
aPUB = aCTX.socket( zmq.PUB )
aPUB.setsockopt(    zmq.LINGER,          0 )   # protect your server
aPUB.setsockopt(    zmq.MAXMSGSIZE,      m )   # protect your server from DoS
aPUB.setsockopt(    zmq.AFFINITY,        3 )   # protect your server resources
aPUB.setsockopt(    zmq.HEARTBEAT_IVL,   ivl ) #     server L3-helper Heartbeats
aPUB.setsockopt(    zmq.HEARTBEAT_TTL,   ttl ) #     server L3-helper Heartbeats
aPUB.setsockopt(    zmq.INVERT_MATCHING, 1 )   #   avoid server sending data back
aPUB.setsockopt(    zmq.IMMEDIATE,       1 )   # avoid Queueing for dead-ends
aPUB.setsockopt(    zmq.TOS,             tos ) # allow for L3-router TOS-policies
...
#PUB PERFORMANCE & RESOURCES TWEAKING DETAILS GO WAY BEYOND THE SCOPE OF THIS POST
aPUB.bind(   "tcp://*:5557" )                  # expose AccessPoint on tcp://

#PULL --------------------------------------------------------------------------
aPULL = aCTX.socket( zmq.PULL )
aPULL.setsockopt(    zmq.LINGER,          0 )  # protect your server
aPULL.setsockopt(    zmq.MAXMSGSIZE,      m )  # protect your server from DoS
aPULL.setsockopt(    zmq.AFFINITY,        3 )  # protect your server resources
aPULL.setsockopt(    zmq.HEARTBEAT_IVL,   ivl )#     server L3-helper Heartbeats
aPULL.setsockopt(    zmq.HEARTBEAT_TTL,   ttl )#     server L3-helper Heartbeats
...
#PULL PERFORMANCE & RESOURCES TWEAKING DETAILS GO WAY BEYOND THE SCOPE OF THIS POST
aPULL.bind(   "tcp://*:5558" )                 # expose AccessPoint on tcp://
...

#main loop ---------------------------------------------------------------------
pass; notSoftFLAG = True
while notSoftFLAG:
    NOP_SLEEP = 10                            #  set a 10 [ms] sleep in case NOP
    if aPULL.poll( 0 ):                       #  NEVER block/wait
       aMSG = aPULL.recv( zmq.DONTWAIT )      #  NEVER .recv()-s own data back
       #CPY = zmq_msg_copy( &aMSG );          // WARNING ABOUT NATIVE C-API
       #                                      // HANDLING, NEED .COPY()
       #                                      //           NEED .CLOSE()
       aPUB.send( aMSG,   zmq.DONTWAIT )      #  re-PUB-lish to all others but sender
       ...< process aMSG payload on server-side, if needed >...

       NOP_SLEEP = 0                          # !NOP, avoid 10[ms] NOP-loop sleep
       pass

    if aReasonToFlagLoopEXIT:
       notSoftFLAG = False
       ...
       NOP_SLEEP = 0
       pass

    if ...:
       ...
       pass

    sleep( NOP_SLEEP )                        # a soft-real-time controlled sleep on NOP
#main loop ---------------------------------------------------------------------
pass

#########
# ALWAYS:
#          better using context-aware try:/except:/finally:

aRetCODE = [ aSOCK.close() for aSOCK in ( aPUB, aPULL, ) ]
...

aCTX.term()
#   .term()
#########