ZeroMQ 模式,用于基于闲置的工作负载平衡工作
ZeroMQ pattern for load balancing work across workers based on idleness
我有一个生产者和 n 个工作人员,我只想在他们尚未处理工作单元并且我正在努力寻找工作时将工作交给他们好的 zeroMQ 模式。
1) REQ/REP
生产者是请求者并创建到每个工人的连接。它跟踪哪个 worker 忙碌,并轮询空闲的 worker
问题:
- 如何在收到响应通知的同时仍能将新工作发送给空闲的工作人员,而无需在生产者中为每个工作人员分配一个线程?
2) PUSH/PULL
生产者推入所有工作人员提供的一个套接字,工作人员推入生产者监听的另一个套接字。
问题:
- 没有工人闲置的概念,即工作滞后于长时间的工作
3) PUB/SUB
无法启动,因为无法确保工作不会丢失
4) 反转REQ/REP
每个worker都是REQ
端,requests来自producer的工作,完成后发送另一个request工作
问题:
- 生产者必须阻止工作请求,直到有工作(因为每个
recv
必须与 send
配对)。这会阻止工作人员以工作完成作为回应
- 可以用单独的完成通道修复,但生产者仍然需要一些轮询机制来检测新工作并保持在同一线程上。
5) 每个工人 PAIR
每个工作人员都有自己的 PAIR
连接,允许独立发送工作和接收结果
问题:
- 与
REQ
/REP
相同的问题,每个工作线程需要一个线程
尽管 zeroMQ 在幕后 non-blocking/async,但我找不到允许我的代码也异步的模式,而不是在许多专用线程中阻塞或轮询更少的自旋循环。这不是 zeroMQ 的好用例吗?
Push/Pull就是你的答案。
当您在 ZeroMQ 中发送消息时,最初发生的只是它位于队列中等待传递到目的地。成功传输后,将从队列中删除。队列长度有限,但可以通过更改套接字的高水位线来设置。
有 a/some 个后台线程代表您管理所有这些,您对 ZeroMQ API 的调用只是向 that/those 个线程发出指令。套接字连接两端的线程正在协作以编组消息的传输,即发送者不会发送消息,除非接收者可以接收它。
考虑一下这在 push/pull 设置中意味着什么。假设您的一名拉动工人落后了。然后它不会接受消息。这意味着发送给它的消息开始堆积,直到达到高水位线。 ZeroMQ 将不再向那个 pull worker 发送消息。事实上,在 ZeroMQ 中的 AFAIK,队列比其对等方更满的拉式工作人员将收到更少的消息,因此所有工作人员的工作量均等化。
那是什么意思?
发送消息即可。让0MQ为您整理。
虽然没有明确的标志说 'already busy',但如果可以发送消息,那么这意味着某个地方的一些 pull worker 能够接收它仅仅是因为它跟上了工作量。因此,它最适合处理新消息。
存在局限性。如果所有工作人员都已满,则不会发送任何消息,并且当它尝试发送另一条消息时您会在推送中被阻止。您只能(似乎)通过计算 zmq_send() 花费的时间来发现这一点。
不要忘记网络
还需要考虑网络带宽问题。推送中排队的消息将按照收件人使用它们的速度或网络速度(以较慢者为准)进行传输。如果您的网络从根本上来说太慢了,那就是错误的网络。
延迟
当然,消息堆积在缓冲区中代表了延迟。这可以通过将高水位线设置得非常低来限制。
这不会解决高延迟问题,但它会让您发现您有一个问题。如果拉动工人数量不足,低高水位线将导致消息发送 failing/blocking 更快。
实际上我认为在 ZeroMQ 中它会阻塞 push/pull;您必须在调用 zmq_send() 时测量经过的时间,以发现事情是否已经停滞不前。
想过 Nanomsg 吗?
Nanomsg 是 ZeroMQ 的重新启动,涉及其中的同一个人。它有很多我喜欢的地方,最终我认为它将取代 ZeroMQ。它有一些更通用的模式(PAIR 适用于所有传输,与 ZeroMQ 不同)。此外,模式本质上是源代码中的可插入组件,因此模式的开发和集成比在 ZeroMQ 中简单得多。有关于差异的讨论here
哲学讨论
演员模型
ZeroMQ 绝对属于 Actor Model 编程领域。消息被塞入队列/通道/套接字,并在稍后的某个不确定的时间点出现在接收端进行处理。
这种架构的危险在于有可能在不知情的情况下发生死锁。
假设您有一个系统,在该系统中,消息通过进程链双向传递,以一种方式说指令,以另一种方式产生结果。有可能其中一个进程将尝试发送消息,而接收方实际上也在尝试将消息发送回给它。
这只有在队列未满并且可以(暂时)吸收消息,让每个人都可以继续前进时才有效。
但是假设网络由于某种原因暂时变得有点忙,并且延迟了消息传输。消息发送可能会失败,因为已达到高水位线。哎呀!没有人再向任何人发送任何东西!
CSP
Actor 模型的开发,称为通信顺序进程,是为了解决这个问题而发明的。它有一个限制;根本没有消息缓冲。在收件人收到所有数据之前,任何进程都无法完成发送消息。
这样做的理论结果是可以对系统设计进行数学分析并宣布它没有死锁。实际结果是,如果您构建的系统可能会死锁,那么它每次都会死锁。那实际上还不错;它会出现在测试中,而不是 post-部署。
奇怪的是,Microsoft 的任务并行库的文档中暗示了这一点,他们提倡将缓冲区长度设置为零以实现更强大的应用程序。
这就像将 ZeroMQ 高水位线设置为零,但在 zmq_setsockopt() 中,0 表示默认值,而不是空值。默认为非零...
CSP 更适合实时应用程序。可用工作人员的任何短缺都会立即导致无法发送消息(因此您的系统知道它无法跟上实时需求)而不是导致延迟增加,因为数据被套接字等吸收(这要困难得多)去发现)。
不幸的是,我们拥有的几乎所有通信技术(以太网、TCP/IP、ZeroMQ、nanomsg 等)都倾向于 Actor 模型。一切事物在某个地方都有某种缓冲区,无论是 NIC 上的数据包缓冲区还是操作系统中的套接字缓冲区。
因此,要在现实世界中实施 CSP,必须在现有传输之上实施流量控制。这需要工作,而且效率有点低。但是如果一个系统需要它,它绝对是要走的路。
就我个人而言,我希望看到 0MQ 和 Nanomsg 将其作为行为选项采用。
您的问题已通过 ZMQ Guide 中的负载平衡模式解决。这一切都与流量控制有关,同时还能够发送和接收消息。生产者只会向空闲的工人发送工作请求,而工人可以随时发送和接收其他消息,例如中止、关机等
我有一个生产者和 n 个工作人员,我只想在他们尚未处理工作单元并且我正在努力寻找工作时将工作交给他们好的 zeroMQ 模式。
1) REQ/REP
生产者是请求者并创建到每个工人的连接。它跟踪哪个 worker 忙碌,并轮询空闲的 worker
问题:
- 如何在收到响应通知的同时仍能将新工作发送给空闲的工作人员,而无需在生产者中为每个工作人员分配一个线程?
2) PUSH/PULL
生产者推入所有工作人员提供的一个套接字,工作人员推入生产者监听的另一个套接字。
问题:
- 没有工人闲置的概念,即工作滞后于长时间的工作
3) PUB/SUB
无法启动,因为无法确保工作不会丢失
4) 反转REQ/REP
每个worker都是REQ
端,requests来自producer的工作,完成后发送另一个request工作
问题:
- 生产者必须阻止工作请求,直到有工作(因为每个
recv
必须与send
配对)。这会阻止工作人员以工作完成作为回应 - 可以用单独的完成通道修复,但生产者仍然需要一些轮询机制来检测新工作并保持在同一线程上。
5) 每个工人 PAIR
每个工作人员都有自己的 PAIR
连接,允许独立发送工作和接收结果
问题:
- 与
REQ
/REP
相同的问题,每个工作线程需要一个线程
尽管 zeroMQ 在幕后 non-blocking/async,但我找不到允许我的代码也异步的模式,而不是在许多专用线程中阻塞或轮询更少的自旋循环。这不是 zeroMQ 的好用例吗?
Push/Pull就是你的答案。
当您在 ZeroMQ 中发送消息时,最初发生的只是它位于队列中等待传递到目的地。成功传输后,将从队列中删除。队列长度有限,但可以通过更改套接字的高水位线来设置。
有 a/some 个后台线程代表您管理所有这些,您对 ZeroMQ API 的调用只是向 that/those 个线程发出指令。套接字连接两端的线程正在协作以编组消息的传输,即发送者不会发送消息,除非接收者可以接收它。
考虑一下这在 push/pull 设置中意味着什么。假设您的一名拉动工人落后了。然后它不会接受消息。这意味着发送给它的消息开始堆积,直到达到高水位线。 ZeroMQ 将不再向那个 pull worker 发送消息。事实上,在 ZeroMQ 中的 AFAIK,队列比其对等方更满的拉式工作人员将收到更少的消息,因此所有工作人员的工作量均等化。
那是什么意思?
发送消息即可。让0MQ为您整理。
虽然没有明确的标志说 'already busy',但如果可以发送消息,那么这意味着某个地方的一些 pull worker 能够接收它仅仅是因为它跟上了工作量。因此,它最适合处理新消息。
存在局限性。如果所有工作人员都已满,则不会发送任何消息,并且当它尝试发送另一条消息时您会在推送中被阻止。您只能(似乎)通过计算 zmq_send() 花费的时间来发现这一点。
不要忘记网络
还需要考虑网络带宽问题。推送中排队的消息将按照收件人使用它们的速度或网络速度(以较慢者为准)进行传输。如果您的网络从根本上来说太慢了,那就是错误的网络。
延迟
当然,消息堆积在缓冲区中代表了延迟。这可以通过将高水位线设置得非常低来限制。
这不会解决高延迟问题,但它会让您发现您有一个问题。如果拉动工人数量不足,低高水位线将导致消息发送 failing/blocking 更快。
实际上我认为在 ZeroMQ 中它会阻塞 push/pull;您必须在调用 zmq_send() 时测量经过的时间,以发现事情是否已经停滞不前。
想过 Nanomsg 吗?
Nanomsg 是 ZeroMQ 的重新启动,涉及其中的同一个人。它有很多我喜欢的地方,最终我认为它将取代 ZeroMQ。它有一些更通用的模式(PAIR 适用于所有传输,与 ZeroMQ 不同)。此外,模式本质上是源代码中的可插入组件,因此模式的开发和集成比在 ZeroMQ 中简单得多。有关于差异的讨论here
哲学讨论
演员模型
ZeroMQ 绝对属于 Actor Model 编程领域。消息被塞入队列/通道/套接字,并在稍后的某个不确定的时间点出现在接收端进行处理。
这种架构的危险在于有可能在不知情的情况下发生死锁。
假设您有一个系统,在该系统中,消息通过进程链双向传递,以一种方式说指令,以另一种方式产生结果。有可能其中一个进程将尝试发送消息,而接收方实际上也在尝试将消息发送回给它。
这只有在队列未满并且可以(暂时)吸收消息,让每个人都可以继续前进时才有效。
但是假设网络由于某种原因暂时变得有点忙,并且延迟了消息传输。消息发送可能会失败,因为已达到高水位线。哎呀!没有人再向任何人发送任何东西!
CSP
Actor 模型的开发,称为通信顺序进程,是为了解决这个问题而发明的。它有一个限制;根本没有消息缓冲。在收件人收到所有数据之前,任何进程都无法完成发送消息。
这样做的理论结果是可以对系统设计进行数学分析并宣布它没有死锁。实际结果是,如果您构建的系统可能会死锁,那么它每次都会死锁。那实际上还不错;它会出现在测试中,而不是 post-部署。
奇怪的是,Microsoft 的任务并行库的文档中暗示了这一点,他们提倡将缓冲区长度设置为零以实现更强大的应用程序。
这就像将 ZeroMQ 高水位线设置为零,但在 zmq_setsockopt() 中,0 表示默认值,而不是空值。默认为非零...
CSP 更适合实时应用程序。可用工作人员的任何短缺都会立即导致无法发送消息(因此您的系统知道它无法跟上实时需求)而不是导致延迟增加,因为数据被套接字等吸收(这要困难得多)去发现)。
不幸的是,我们拥有的几乎所有通信技术(以太网、TCP/IP、ZeroMQ、nanomsg 等)都倾向于 Actor 模型。一切事物在某个地方都有某种缓冲区,无论是 NIC 上的数据包缓冲区还是操作系统中的套接字缓冲区。
因此,要在现实世界中实施 CSP,必须在现有传输之上实施流量控制。这需要工作,而且效率有点低。但是如果一个系统需要它,它绝对是要走的路。
就我个人而言,我希望看到 0MQ 和 Nanomsg 将其作为行为选项采用。
您的问题已通过 ZMQ Guide 中的负载平衡模式解决。这一切都与流量控制有关,同时还能够发送和接收消息。生产者只会向空闲的工人发送工作请求,而工人可以随时发送和接收其他消息,例如中止、关机等