High-Water Marks
当您可以从一个进程快速地向另一个进程发送消息时,您很快就会发现内存是一种宝贵的资源,可以轻松地填满它。除非您了解问题所在并采取预防措施,否则流程中的某个地方的几秒钟延迟可能会变成一个导致服务器崩溃的积压。
问题是这样的:假设进程A以高频率发送消息给正在处理它们的进程B。突然B变得非常繁忙(垃圾收集、CPU过载等等),短时间内无法处理消息。一些严重的垃圾收集可能需要几秒钟,如果有更严重的问题,则可能需要更长的时间。进程A仍然试图疯狂发送的消息发生了什么?有些将位于B的网络缓冲区中。有些会直接连接到以太网上。有些将位于A的网络缓冲区中。剩下的就会在A的内存中累积,就像A后面的应用程序发送它们一样快。如果不采取一些预防措施,A很容易耗尽内存并崩溃。
这是消息代理的一个经典问题。更糟糕的是,从表面上看,这是B的错,而B通常是一个用户编写的应用程序,A无法控制它。
答案是什么?一种方法是将问题逆流而上。A从其他地方接收信息。所以告诉这个过程,“停止!”等等。这叫做流量控制。这听起来很有道理,但如果你在推特上发消息呢?你会告诉全世界在B振作起来的时候不要再发推特吗?
流量控制在某些情况下有效,但在其他情况下无效。传输层不能告诉应用层“停止”,就像地铁系统不能告诉大企业“请让你的员工再工作半小时, 我太忙了”一样。消息传递的答案是设置缓冲区大小的限制,然后当我们达到这些限制时,采取一些合理的行动。在某些情况下,答案是丢弃消息。在另一些时候,最好的策略是等待。
ZeroMQ使用HWM(high-water mark)的概念来定义其内部管道的容量。从套接字到套接字的每个传入/传出连接都有自己的管道,根据套接字的类型,HWM用于发送和(或)接收。一些套接字(PUB, PUSH)只有发送缓冲区。有些(SUB, PULL, REQ, REP)只有接收缓冲区。有些(DEALER, ROUTER, PAIR)同时具有发送和接收缓冲区。
当套接字到达它的HWM时,它将根据套接字类型阻塞或丢弃数据。PUB和ROUTER套接字将在数据到达HWM时丢弃数据,而其他类型的套接字将阻塞数据。在inproc传输中,发送方和接收方共享相同的缓冲区,因此实际的HWM是双方设置的HWM的和。
最后,hwm并不精确;虽然默认情况下您可能会得到多达1,000条消息,但由于libzmq实现其队列的方式,实际缓冲区大小可能要低得多(只有一半)。