通过ZeroMQ文档阅读,当我发现这三个套接字组合时,我有点失落。他们是:
据我所知,DEALER和ROUTER是同步REQ / REP通信的替代品,因此它们变为异步,多个节点可以连接。我不明白的是,经销商可以如何替代REQ和DEALER到DEALER的REP(以及ROUTER到ROUTER中的路由器)。
我一直在寻找一种模式,允许任意数量的客户端将作业提交给任意数量的工作者(处理负载平衡)并将响应(和中间结果)返回给客户端(异步,但发送多条消息)背部)。客户端可能还需要能够提前终止工作。我发现documentation在这方面有点亮(我不是一个专家,可能错过了相关部分)。
我很高兴自己弄清楚细节,但每次我认为我找到了合适的模式,我发现另一个可能同样合适的模式(例如,这3种模式在我看来同样适用:{{3 }},http://zguide.zeromq.org/page:all#ROUTER-Broker-and-REQ-Workers,http://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
项目相同?我理解路由器工作的方式是分配它跟踪的随机身份。这是如何工作的(它似乎在一个小规模的例子中起作用)?答案 0 :(得分:4)
除了我的更新,我发现工作人员可以使用经销商连接到服务器的后端。可以找到模式和解释here。
客户端使用DEALER套接字,服务器在前端(asyn +许多客户端)接收请求作为ROUTER,使用DEALER套接字(asyn)将它们代理到工作者(后端),并且工作人员监听服务器&# 39;在DEALER套接字上的后端(asyn,不需要路由,尽管ROUTER也有效)。
如果工人严格同步,我们会使用REP,但是因为我们 想要发送多个回复,我们需要一个异步套接字。我们不想要 路由回复;他们总是去发送的单个服务器线程 我们的要求。
进一步的修改是用while True
broker()
中的{{1}}循环替换路由器/经销商消息的隐式分派。
<强>更新强>
显然,这种模式使用ZMQ的标准循环路由。可以通过ROUTER到ROUTER模式实现自定义任务分配。在这种情况下,客户端从发送请求开始,工作人员通过发送就绪消息开始。代理管理就绪工作者列表,如果没有,则关闭新客户端消息的轮询(因此使用ZMQ的内部消息缓冲区)。