ZeroMQ/ZMQ的Push/Pull模式的实用性

51
在尝试使用ZeroMQ Push/Pull(也被称为Pipeline)套接字类型时,我很难理解这种模式的实用性。它被描述为一种“负载均衡器”。 对于一个单一的服务器向多个工作者发送任务,Push/Pull将在所有客户端之间平均分配任务。例如有3个客户端和30个任务,每个客户端获得10个任务:client1获得任务1、4、7...,client2获得2、5...以此类推。很公平,但实际上通常存在任务复杂度或客户端计算资源的异构混合(或可用性),此时这种模式就会出现严重问题。所有任务似乎都是预定好的,而服务器不知道客户端的进度或是否可用。如果client1崩溃,则其剩余的任务不会被分配给其他客户端,而是保留在client1队列中。如果client1仍然无法正常运行,那么这些任务将永远没有人处理。相反,如果某个客户端更快地处理其任务,则它不会获得更多任务并保持空闲,因为这些任务保留给其他客户端安排。 使用REQ/REP是一种可能的解决方案;任务只分配给可用资源。 那么我是错过了什么吗?如何有效地使用Push/Pull呢?有没有一种方法可以处理客户端、任务等的不对称性,使用此套接字类型? 谢谢!以下是一个简单的Python示例:
# server

import zmq
import time

context = zmq.Context()
socket = context.socket(zmq.PUSH)
#socket = context.socket(zmq.REP)   # uncomment for Req/Rep

socket.bind("tcp://127.0.0.1:5555")

i = 0
time.sleep(1)   # naive wait for clients to arrive

while True:
  #msg = socket.recv()    # uncomment for Req/Rep
  socket.send(chr(i))
  i += 1 
  if i == 100:
    break

time.sleep(10)   # naive wait for tasks to drain

.

# client

import zmq
import time
import sys

context = zmq.Context()

socket = context.socket(zmq.PULL)
#socket = context.socket(zmq.REQ)    # uncomment for Req/Rep

socket.connect("tcp://127.0.0.1:5555")

delay = float(sys.argv[1])

while True:
  #socket.send('')     # uncomment for Req/Rep
  message = socket.recv()
  print "recv:", ord(message)
  time.sleep(delay)

在命令行上启动三个客户端,带有延迟参数(即1、1和0.1),然后启动服务器,观察所有任务如何均匀分布。 然后关闭其中一个客户端,以查看其剩余任务未被处理。

取消注释所示的行,将其切换到Req/Rep类型的套接字,并观察更有效的负载平衡器。

1个回答

69

它不是一个负载均衡器,这是0MQ文档中存在了一段时间的错误解释。要进行负载均衡,必须从工作进程那里获取有关其可用性的一些信息。PUSH(与DEALER类似)是一种循环分发器。它以其原始速度和简单性而闻名。您不需要任何交流,只需将任务下推到管道中,它们就会迅速地传递到所有可用的工作进程。

当您执行大量小任务且工作进程不经常加入或退出时,此模式非常有用。该模式对于需要时间完成的较大任务并不好,因为这时您需要一个单个队列,仅将新任务发送给可用的工作进程。它还存在一个反模式,即如果客户端发送了许多任务然后工作人员连接,则第一个工作人员将抓取1,000个或更多消息,而其他工作人员仍在忙于连接。

您可以用几种方法创建自己的高级路由。查看指南中的LRU模式:在其中,工作人员明确告诉代理“准备好了”。您还可以进行基于信用的流控制,在任何真正的负载均衡情况下都可以这样做。这是LRU模式的概括。请参阅http://hintjens.com/blog:15


当一个工作进程失败时,是否有机制来检测并恢复已分配但未发送的排队任务?类似于具有任务重新分配功能的超时机制。 - CNK
9
如果您想检测失败的工人,您需要自己添加这个功能。这相对容易:收集所有结果,如果有一个结果丢失,重新启动整个批处理。由于故障很少发生,这种简单而粗暴的方法可以很好地处理它。 - Pieter Hintjens
链接已经失效。 - kawing-chiu
如果我们使用 ZMQ_HWM 选项,在所有下游 puller 上将其设置为一个较小的数字,那么这样不会强制 pusher 将所有请求发送到连接的第一个 puller 吗? - smac89

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接