刚学习RabbitMQ的同学可能对prefetchCount预取值这个参数有一些困惑,本文对其作用进行详细讲解。配合了几张有趣的图片方便同学们理解。

一、从轮询分发说起

  首先要从轮询分发机制讲起,轮询分发顾名思义,依次发消息,生产者不会管你消费者消费消息的速度,我只负责把消息依次给你们,假设我有两个消费者C1和C2,生产者发出四条消息11,22,33,44;我们可以认为,11和33将由消费者C1处理,22和44将由C2处理,谁处理得快,谁处理得慢,生产者不在意,生产者一有消息就发,挨个拼命地发。   那么这会导致一个什么问题,不知道大家有没有思考过,当生产者这边突然有大量消息的时候,假如有10000000条消息,而两个消费者处理消息的速度是不够快的,例如都是1秒1条,而生产者发过来的这些消息是要存储在消费者的JVM堆内存中的,那么在轮询的这样的机制下,生产者不会管你消费者那边JVM堆内存够不够,会不会爆内存,他都一直这样发给消费者,而消费者担心消息丢失,他从生产者那边接收到的每一条消息都得保存到自己的堆内存中,很可能就因为消息数量太多就OOM(Out of Memory)了。   当然,这种情况我说的比较夸张,实际中可能有的消费者处理速度快,有的消费者处理速度慢,那么慢的那个就更有可能发生JVM堆内存爆掉的情况。

二、不公平分发

  很显然,上面这种轮询分发的方法,根本不可取,因为这种方法首先没有考虑到消费者处理消息的速度,无论快慢,都给所有消费者等量的消息,会造成速度快的消费者资源空闲(没有消息给他处理),速度慢的消费者JVM堆内存不足。   那么我们想到一种改进的方式,设置一个名为prefetchCount的参数,例如我设置为1,就代表当我的消费者在同一时间只能处理一条消息,当消费者在处理消息时,生产者就不要把消息发给我,占用我的JVM堆内存,想想这样会得到什么样的效果?速度快的消费者处理完一条,接着去队列拿消息,速度慢的消费者同样如此,只是它消费的消息数量更少一些而已。看上去这种方式非常合理,但如果我们进一步考虑到吞吐量的问题,消费者每次只能处理一条消息,处理完一条,再去拿一条,假如消息普遍比较大,并且服务端距离客户端距离非常远,那么消息的网络传输肯定需要不少时间,极端一点,我假设消费者小C处理消息的速度极快(小C表示,根本不够打好不好?我要打10个!),0.01秒一条,而每条消息传输时间需要1秒钟,这就会发生消费者处理完消息,等下条消息需要1秒,刚花0.01秒处理完,又是等1秒,计算资源浪费太多了有没有?

三、预取值的设置

  这时候我们预取值的作用就体现出来了,我可以允许小C在同一时间有多条未处理的消息存放在他和队列连接的信道里,比方说有200条,那么处理完一条,就不用再去等待下一条新消息从遥远的生产者(服务端)那边发过来,直接接着处理下一条。同时,生产者那边发现小C这信道里的消息不足200条了,就继续给他发(继续满上),这样可以使得生产者小C一直有消息可以处理,也不会因为消息发过来太多导致小C的JVM内存爆炸。

基于以上几种情形,我们可以做出如下总结:

1.当消费者处理消息的速度很慢,而且队列消息较少的情况下,可以把prefetchCount设置为1。 2.当每条消息的数据很大,或传输距离很远时,即网络传输延迟大时,要计算好客户端能容纳的未确认消息的总量,设置一个合理的预取值。 3.当消费者处理速度极快,远超过服务端分发消息的速度时,在网络带宽足够时,可以设置预取值为0,即不设限。

本文章基于某谷的RabbitMQ教程进行讲解,如有错误,欢迎讲出。

推荐文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: