Q:刚开始是对这个疑问抱有质疑态度的,因为使用消息队列的其中目的就是削峰填谷,来避免高流量时,对下游服务的冲击,所以使用消息队列进行缓冲,下游根据自己的消费能力去消费,
我感觉这就是消息积压本就是使用消息队列的功能,怎么会是问题呢?
A:首先消息积压是正常现象,但凡是过多就不正常了。积压越来越多就需要处理了。就像一个水库,日常蓄水是正常的,但下游泄洪能力太差,导致水库水位一直不停的上涨,这个就不正常了。
消息发送主要涉及三方:producer/consumer/broker,所以发生消息积压要从这三方来看。
1、broker
其实不用太关注消息队列,因为消息队列本身的处理能力要远远大于业务系统的处理能力。主流消息队列的单个节点,消息收发的性能可以达到每秒钟处理几万至几十万条消息的水平,还可以通过水平扩展 Broker 的实例数成倍地提升处理能力。
2、producer端性能优化
producer端对消息积压的影响不大,但是对producer端发送消息的性能有要求。一般是先执行自己的业务逻辑,最后再发送消息。如果说,你的代码发送消息的性能上不去,你需要优先检查一下,是不是发消息之前的业务逻辑耗时太多导致的。
对于发送消息的业务逻辑来说,设置批量发送及批量发送的大小可以提高发送端的发送性能
Producer 发送消息的过程,Producer 发消息给 Broker,Broker 收到消息后返回确认响应,这是一次完整的交互。假设这一次交互的平均时延是 1ms,我们把这 1ms 的时间分解开,它包括了下面这些步骤的耗时:发送端准备数据、序列化消息、构造请求等逻辑的时间,也就是发送端在发送网络请求之前的耗时;发送消息和返回响应在网络传输中的耗时;Broker 处理消息的时延。如果是单线程发送,每次只发送 1 条消息,那么每秒只能发送 1000ms / 1ms * 1 条 /ms = 1000 条 消息,这种情况下并不能发挥出消息队列的全部实力。
无论是增加每次发送消息的批量大小,还是增加并发,都能成倍地提升发送性能。至于到底是选择批量发送还是增加并发,主要取决于发送端程序的业务性质。
发生消息积压后,producer端服务降级,关闭一些非核心业务,减少消息的产生。
3、consumer端性能优化
发生消息积压,主要原因就是消费端的消费能力跟不上生产端的生产速度。
扩容consumer实例,注意扩容后必须同步扩容主题中的分区(也叫队列)数量,确保 Consumer 的实例数和分区数量是相等的,因为在队列中的消费是单线程的,如果只
扩容了consumer实例,没有扩容队列数量,则不会有效果的。
例如 Kafka,这个消息积压的 Topic 有 3 个 Partition,那最多就能用 3 个 Consumer,所以增加 Consumer 没有用。
还是可以使用临时队列的方式作为中转,提升处理能力。
等待积压的消息被消费,恢复到正常状态,撤掉扩容服务器。
-
假如你的消费者出故障了,而生产者还是不停的往mq中写入数据,积压了几百万甚至上千万数据了,磁盘都快满了,怎么解决?
申请一批机器,修改消费者代码,不做任何处理,把消息写入新部署的中,等消费者bug处理好消费.
修改消费者代码,消费mq数据,不做任何处理直接丢弃,后面手动写程序找出丢失的数据,手动重新发送到mq中. -
当你的消费者处理完bug重新上线,你的每个消费者每秒能处理1000条数据,有3个消费者则每秒能处理3000条数据,一分钟则是18万条数据,那么处理完这上千万的数据也需要1个多小时了,如何能快速消费积压的数据?
假设原先有3个消费者,对应则是有3个partition,此时新建一个topic,这个topic中的partition是原来十倍,原先的三个消费者修改代码,将消费到的数据不在任何处理,直接写入新topic中,然后向公司申请一批机器,对应30个partition部署30个消费者去处理数据.原先3个消费者需要一个小时左右处理完积压的数据,此时30个零时消费者则10左右就能处理完积压的数据.
-
mq设置了过期时间,导致积压的大量数据搞丢了,怎么办?
正常情况下mq是不允许设置过期时间的,这样会很坑,如果遇到这种情况,只能手动写程序找出丢失的数据,手动重新发送到mq中.