在我的理解中rabbitmq的qos设置对于我们mq队列的速度和性能方面有一定的影响
假如消费者都down机了,或者生产者生产的数量越来越多,队列拼命堆积,如果不设置QOS,MQ默认perfercount是1,所以可能消费者一启动,一堆消息拼命挤到消费者中,然后可能会造成程序down机。
设置某个队列消费每次拉取1条消息channel.basicQos(1)
从源码知道
假如设置0就说明没有限制的拉取
合理的设置QOS对于系统性能也是有很多的提高
默认QoS prefetch设置为客户端提供了无限的 缓冲区,这可能导致不良行为和性能。但是你应该将QoS prefetch缓冲区大小设置为什么?目标是让消费者保持饱和的工作,但最大限度地减少客户的缓冲区大小,以便更多的消息留在Rabbit的队列中,因此可供新消费者使用
假设Rabbit需要50ms才能从这个队列中获取消息,将其放在网络上并让它到达消费者手中。客户端处理消息需要4ms。一旦消费者处理完消息,它就会发送ack回兔子,然后又需要花费50ms才能发送给Rabbit并由Rabbit处理。所以我们的总往返时间为104毫秒。如果我们的QoS prefetch设置为1消息,则Rabbit将在此往返完成之前发送下一条消息。因此,客户端将每104ms仅占4ms,或占3.8%。我们希望它在100%的时间都很忙。
从队列中消费
如果我们在客户端为每条消息做总的往返时间 / 处理时间,我们得到104 / 4 = 26。如果我们有prefetch26条消息的QoS ,这就解决了我们的问题:假设客户端有26条消息被缓冲,准备就绪并等待处理。(这是一个明智的假设:一旦你设定basic.qos然后consume从队列中,Rabbit将从您订阅的队列中发送尽可能多的消息到客户端,直到达到QoS限制。如果您假设消息不是很大并且带宽很高,那么Rabbit可能会比您的客户端处理它们更快地向您的消费客户端发送消息。因此,假设一个完整的客户端缓冲区来完成所有数学运算是合理的(也更简单)。)如果每条消息需要4ms的处理时间,则需要26 * 4 = 104ms处理整个缓冲区。前4ms是第一条消息的客户端处理。然后客户发出一个ack然后继续处理来自缓冲区的下一条消息。那个ack需要50ms才能到达经纪人。代理向客户端发出新消息,这需要50ms到达那里,所以当104ms已经过去并且客户端已经完成处理其缓冲区时,来自代理的下一条消息已经到达并准备就绪并等待客户来处理它。因此,客户端始终保持忙碌:拥有prefetch更高的QoS 不会使其更快; 但我们最大限度地减少了缓冲区大小,从而最大限度地减少了客户端中消息的延迟:客户端缓冲消息的时间不超过它们所需的时间,以保持客户端的工作饱和度。实际上,客户端能够在下一个消息到达之前完全耗尽缓冲区,因此缓冲区实际上保持为空。
如果处理时间和网络行为保持不变,这个解决方案绝对没问题。但是考虑一下,如果网络速度突然减半会发生什么:你的prefetch缓冲区不再足够大,现在客户端将处于空闲状态,等待新消息到达,因为客户端能够比Rabbit提供新消息更快地处理消息。
为了解决这个问题,我们可能只决定将QoS prefetch大小加倍(或几乎加倍)。如果我们从26将它推到51,那么如果客户端处理仍然是每个消息4ms,我们现在51 * 4 = 204ms在缓冲区中有消息,其中4ms用于处理消息,留下200ms用于发送ack回到Rabbit并且收到下一条消息。因此,我们现在可以应对网络速度减半。
但是,如果网络正常运行,prefetch 现在加倍我们的QoS 意味着每条消息将在客户端缓冲区中停留一段时间,而不是在到达客户端时立即处理。同样,从现在51个消息的完整缓冲区开始,我们知道在客户端完成处理第一个消息后,新消息将在客户端100ms处开始出现。但是在那100毫秒中,客户端将处理100 / 4 = 2550个可用的消息。这意味着当新消息到达客户端时,它将被添加到缓冲区的末尾,因为客户端从缓冲区的头部移除。因此,缓冲区将始终保留50 - 25 = 25 较长的消息,因此每条消息都将位于缓冲区中25 * 4 = 100ms,增加Rabbit发送到客户端和客户端开始处理它从50ms到150ms之间的延迟。
因此,我们看到增加prefetch缓冲区以便客户端可以在保持客户端忙碌的同时应对恶化的网络性能,从而在网络正常运行时大大增加延迟。
同样地,如果客户端开始花费40ms来处理每条消息而不是4ms,那么网络的性能会不会恶化,而不是网络性能的恶化?如果Rabbit中的队列之前处于稳定长度(即入口和出口速率相同),它现在将开始快速增长,因为出口率已下降到原来的十分之一。您可能决定尝试通过添加更多消费者来处理这一不断增长的积压工作,但现有客户端现在正在缓冲消息。假设原始缓冲区大小为26条消息,客户端将花费40ms处理第一条消息,然后将ack发送回Rabbit并转移到下一条消息。ack仍需要50ms才能到达Rabbit,而另外50ms用于发送新消息,但在100ms内,客户端只能通过100 / 40 = 2.5进一步的消息而不是剩下的25条消息。因此缓冲区此时25 - 3 = 22消息很长。来自Rabbit的新消息,而不是立即处理,现在位于第23位,其他22条消息仍在等待处理,并且客户端不会再触及该消息22 * 40 = 880ms。鉴于从Rabbit到客户端的网络延迟仅为50ms,此额外的880ms延迟现在是延迟(880 / (880 + 50) = 0.946)的95%。
更糟糕的是,如果我们将缓冲区大小加倍到51条消息以应对网络性能下降会发生什么?处理完第一条消息后,客户端将缓冲50条其他消息。100ms之后(假设网络正常运行),一条新消息将从Rabbit到达,客户端将处理这50条消息中的第3条(缓冲区现在将是47条消息),因此新消息将在缓冲区中排在第48位,不再触及47 * 40 = 1880ms。再次,考虑到将消息发送到客户端的网络延迟仅为50ms,现在进一步延迟1880ms意味着客户端缓冲导致超过97%的延迟(1880 / (1880 + 50) = 0.974)。这可能是不可接受的:如果数据被及时处理,而不是在客户收到数据后的2秒内,数据可能只有效且有用!如果其他消费客户端闲置,他们无能为力:一旦Rabbit向客户端发送消息,该消息就是客户端的责任,直到它确认或拒绝该消息。一旦将消息发送到客户端,客户端就不能互相窃取消息。您想要的是让客户端保持忙碌,但是客户端要尽可能少地缓冲消息,这样消息就不会被客户端缓冲区延迟,因此可以快速地从Rabbit队列中获取新的消费客户端消息。
因此,如果网络变慢,缓冲区太小会导致客户端空闲,但如果网络正常运行,则缓冲区太大会导致额外的延迟,如果客户端突然开始花费更长的时间处理每个延迟,则会产生大量的额外延迟消息比正常。很明显,你真正想要的是不同的缓冲区大小。这些问题在网络设备中很常见,并且已成为许多研究的主题。主动队列管理算法试图尝试删除或拒绝消息,以避免长时间驻留在缓冲区中的消息。当缓冲区保持为空时(每个消息仅受网络延迟而且根本不在缓冲区中),并且缓冲区可以吸收峰值,从而实现最低延迟。吉姆盖蒂斯从网络路由器的角度来看,这个问题一直在研究这个问题:局域网和广域网性能之间的差异正在遭受同样的问题。实际上,只要生产者(在我们的例子中是Rabbit)和消费者(客户端应用程序逻辑)之间有一个缓冲区,其中双方的性能可以动态变化,你就会遇到这些问题。最近出版 了一种称为受控延迟的新算法,该算法 在解决这些问题方面似乎很有效。
作者声称他们的CoDel(“coddle”)算法是一种“无旋钮”算法。这真是一个谎言:有两个旋钮,他们确实需要适当的设置。但是每次性能变化都不需要改变,这是一个巨大的好处。我已经 为我们的AMQP Java客户端实现了这个算法,作为QueueingConsumer的一个变种。虽然原始算法针对的是TCP层,但只有丢弃数据包才有效(TCP本身会负责重新传输丢失的数据包),而AMQP则不那么礼貌!因此,我的实现使用Rabbit的basic.nack扩展来显式地将消息返回到队列,以便其他人可以处理它们。
使用它与普通的QueueingConsumer 几乎相同,只是你应该为构造函数提供三个额外的参数以获得最佳性能。
第一个是requeue说,当消息被剔除时,它们是否应该被重新排队或丢弃。如果错误,它们将被丢弃,如果它们被设置,可能会触发死信交换机制。
第二个是targetDelay消息在客户端QoS prefetch 缓冲区中等待的可接受时间(以毫秒为单位)。
第三个是interval和,是一个消息的预期最坏情况处理时间(以毫秒为单位)。这不一定是现场,但在一个数量级内肯定有帮助。
您仍应prefetch适当地设置QoS 大小。如果不这样做,那么客户端可能会发送大量消息,如果它们在缓冲区中停留太长时间,则算法必须将它们返回给Rabbit。当消息返回给Rabbit时,很容易导致大量额外的网络流量。CoDel算法只是在性能偏离标准时才开始丢弃(或拒绝)消息,因此一个有效的例子可能有所帮助。
假设每个方向的网络遍历时间为50ms,我们希望客户端平均每个消息处理4ms,但这可能会达到20ms。因此,我们将intervalCoDel 的参数设置为20.有时网络的速度减半,因此每个方向的遍历时间可以是100ms。为了迎合这一点,我们设置 basic.qos prefetch为204 / 4 = 51。是的,这意味着当网络正常运行时,缓冲区将在大多数情况下长时间保留25条消息(参见前面的工作),但我们认为没问题。因此,每条消息都将位于预期的缓冲区中25 * 4 = 100ms,因此我们将targetDelayCoDel 设置为100。
当事情正常运行时,CoDel不应该妨碍,并且很少有任何消息应该被扼杀。但是,如果客户端开始处理消息的速度比正常情况慢,CoDel会发现消息已被客户端缓冲了太长时间,并将这些消息返回到队列中。如果这些消息被重新排队,那么它们将可以交付给其他客户。
这在目前非常具有实验性,并且可以看出为什么CoDel不适合处理AMQP消息和普通IP的原因。还值得记住的是,通过nacks重新排队消息是一项相当昂贵的操作,因此最好设置CoDel的参数,以确保在正常操作中很少有消息被捕获。管理插件是一种简单的方法,可以检查有多少邮件正在被删除。与以往一样,欢迎评论,反馈和改进!
此条目发布于2012年5月11日星期五下午12:17由Matthew Sackman发布,并在HowTo,New Features下提交。您可以通过RSS 2.0 Feed 关注此条目的任何回复 。双方的意见和坪目前封闭。
参考文档 https://www.rabbitmq.com/blog/2012/05/11/some-queuing-theory-throughput-latency-and-bandwidth/