ZMQ客户端 - 工作者通信模式

时间:2015-04-23 17:48:44

标签: sockets messaging zeromq

通过ZeroMQ文档阅读,当我发现这三个套接字组合时,我有点失落。他们是:

  • 经销商ROUTER
  • 交易经销商
  • ROUTER to ROUTER

据我所知,DEALER和ROUTER是同步REQ / REP通信的替代品,因此它们变为异步,多个节点可以连接。我不明白的是,经销商可以如何替代REQ和DEALER到DEALER的REP(以及ROUTER到ROUTER中的路由器)。

我一直在寻找一种模式,允许任意数量的客户端将作业提交给任意数量的工作者(处理负载平衡)并将响应(和中间结果)返回给客户端(异步,但发送多条消息)背部)。客户端可能还需要能够提前终止工作。我发现documentation在这方面有点亮(我不是一个专家,可能错过了相关部分)。

我很高兴自己弄清楚细节,但每次我认为我找到了合适的模式,我发现另一个可能同样合适的模式(例如,这3种模式在我看来同样适用:{{3 }},http://zguide.zeromq.org/page:all#ROUTER-Broker-and-REQ-Workershttp://zguide.zeromq.org/page:all#ROUTER-Broker-and-DEALER-Workers)。

对结构的任何建议(要与哪个组件进行通信的套接字)都很受欢迎。

更新

这是我到目前为止所提出的:

import multiprocessing
import zmq
import time

router_url_b = 'tcp://*:5560'
router_url = 'tcp://localhost:5560'

dealer_url_b = 'tcp://*:5561'
dealer_url = 'tcp://localhost:5561'


def broker():
    context = zmq.Context()
    router = context.socket(zmq.ROUTER)
    router.bind(router_url_b)

    dealer = context.socket(zmq.DEALER)
    dealer.bind(dealer_url_b)

    poll = zmq.Poller()
    poll.register(router, zmq.POLLIN)
    poll.register(dealer, zmq.POLLIN)

    while True:
        poll_result = dict(poll.poll())
        if router in poll_result:
            ident, msg = router.recv_multipart()
            print 'router: ident=%s, msg=%s' % (ident, msg)
            # print 'router received "%s" and ident %s' % (msg, ident)
            dealer.send_multipart([ident, msg])
            # dealer.send(msg)
        if dealer in poll_result:
            ident, msg = dealer.recv_multipart()
            print 'dealer: ident=%s, msg=%s' % (ident, msg)
            router.send_multipart([ident, msg])


def client(client_id):
    context = zmq.Context()
    req = context.socket(zmq.DEALER)
    # setting identity doesn't seem to make a difference
    req.setsockopt(zmq.IDENTITY, b"%s" % client_id)
    req.connect(router_url)

    req.send('work %d' % client_id)
    while True:
        msg = req.recv()
        print 'client %d received response: %s' % (client_id, msg)


def worker(worker_id):
    context = zmq.Context()
    # to allow asynchronous sending of responses.
    rep = context.socket(zmq.ROUTER)
    # not sure if this is required...
    # rep.setsockopt(zmq.IDENTITY, b"%s" % (10+worker_id))
    rep.connect(dealer_url)

    while True:
        msg = rep.recv_multipart()
        ident, msg = msg[:-1], msg[-1]
        print 'worker %d received: "%s", ident="%s"' % (worker_id, msg, ident)
        # do some work...
        time.sleep(10)
        rep.send_multipart(ident + ['result A from worker %d (%s)' % (worker_id, msg)])
        # do more work...
        time.sleep(10)
        rep.send_multipart(ident + ['result B from worker %d (%s)' % (worker_id, msg)])
    print 'finished worker', worker_id


def main():

    print 'creating workers'
    for i in xrange(2):
        p = multiprocessing.Process(target=worker, args=(i, ))
        p.daemon = True
        p.start()

    print 'creating clients'
    for i in xrange(5):
        p = multiprocessing.Process(target=client, args=(i, ))
        p.daemon = True
        p.start()

    broker()


if __name__ == '__main__':
    main()

它似乎工作得很好。只有工作人员开始处理工作时,才会缺少从客户端到工作人员的通信。我想最好的想法是创建一个新的控制通道(pub / sub),以便在需要时终止工作。

还有一些问题:

  • 这个模型有任何明显的弱点吗?
  • IDENTITY对什么有用?如果我设置这些值(无论是在客户端还是在工作者中)似乎并不重要。
  • 工人收到的第一条消息是: worker 1 received: "work 3", ident="['\x00\x80\x00A\xa7', '3']" worker 0 received: "work 4", ident="['\x00\x80\x00A\xa7', '4']" 为什么两个工人的第一个ident项目相同?我理解路由器工作的方式是分配它跟踪的随机身份。这是如何工作的(它似乎在一个小规模的例子中起作用)?

1 个答案:

答案 0 :(得分:4)

除了我的更新,我发现工作人员可以使用经销商连接到服务器的后端。可以找到模式和解释here

客户端使用DEALER套接字,服务器在前端(asyn +许多客户端)接收请求作为ROUTER,使用DEALER套接字(asyn)将它们代理到工作者(后端),并且工作人员监听服务器&# 39;在DEALER套接字上的后端(asyn,不需要路由,尽管ROUTER也有效)。

  

如果工人严格同步,我们会使用REP,但是因为我们   想要发送多个回复,我们需要一个异步套接字。我们不想要   路由回复;他们总是去发送的单个服务器线程   我们的要求。

进一步的修改是用while True broker()中的{{1}}循环替换路由器/经销商消息的隐式分派。

<强>更新

显然,这种模式使用ZMQ的标准循环路由。可以通过ROUTER到ROUTER模式实现自定义任务分配。在这种情况下,客户端从发送请求开始,工作人员通过发送就绪消息开始。代理管理就绪工作者列表,如果没有,则关闭新客户端消息的轮询(因此使用ZMQ的内部消息缓冲区)。