07 | 消息积压了该如何处理?
消息积压的直接原因:一定是系统中的某个部分出现了性能问题,来不及处理上游发送的消息,才会导致消息积压。
优化性能来避免消息积压
性能优化,主要集中在生产者和消费者这两部分的业务逻辑中。对于消息队列本身的性能,不需要太关注。主要原因是,消息队列本身的处理能力要远大于业务系统的处理能力。
1、发送端性能优化
发送端先执行自己的业务逻辑,最后再发送消息。代码发送消息的性能差,首先检查是不是发消息之前的业务逻辑耗时太多导致的。
解决思路:
- 设置合适的并发和批量大小;增加每次发送消息的批量大小,还是增加并发;
- 注重时延的采取增加并发,不注重的采取增加批量大小;
举例:
微服务,主要接受 RPC 请求处理在线业务 ,批量发送必然会影响 RPC 服务的时延。适合通过增加并发来提升发送性能。
离线分析系统,不关心时延,更注重整个系统的吞吐量。更适合增加批量发送大小。
2、消费端性能优化
大部分的性能问题都出现在消费端,当消费的速度跟不上发送端生产消息的速度,就会造成消息积压。
注:设计系统的时候,一定要保证消费端的消费性能要高于生产端的发送性能,这样的系统才能健康的持续运行。
优化思路:
- 优化消费业务逻辑;
- 通过水平扩容,增加消费端的并发数来提升总体的消费性能。
注:在扩容 Consumer 的实例数量的同时,必须同步扩容主题中的分区(也叫队列)数量,确保 Consumer 的实例数和分区数量是相等的。如果 Consumer 的实例数量超过分区数量,这样的扩容实际上是没有效果的。因为对于消费者来说,在每个分区上实际上只能支持单线程消费。
消息积压了该如何处理?
情况:正常运转的时候,没有积压或者只有少量积压很快就消费掉了,但是某一个时刻,突然就开始积压消息并且积压持续上涨。
排查思路:发送变快了,要么是消费变慢了。
- 单位时间发送的消息增多,赶上大促或者抢购,可以通过扩容消费端的实例数来提升总体的消费能力。
- 短时间内没有足够的服务器资源进行扩容,可以将系统降级,关闭一些不重要的业务,减少发送方发送的数据量。
- 发送消息的速度还是消费消息的速度都没什么变化,需要检查一下你的消费端,是不是消费失败导致的一条消息反复消费这种情况比较多,这种情况也会拖慢整个系统的消费速度。
- 如果监控到消费变慢了,需要检查消费实例,优先检查一下日志是否有大量的消费错误,如果没有错误的话,可以通过打印堆栈信息,看一下消费线程是不是卡在什么地方不动了,比如触发了死锁或者卡在等待某些资源上了。
消费端是否可以通过批量消费的方式来提升消费性能
1.无法提升消费业务效率(仅受消费业务自身逻辑影响),但可以提高mq中堆积消息消费的整体吞吐量(批推比单推mq耗时较短)。
2.批处理意味数据积累和大数据传输,这会让单次消费的最长时延变长。同时批量操作为了保证当前批量操作一致性,在个别失败的情况下会引发批量操作重试。
08 | 答疑解惑(一)
1、网关如何接收服务端的秒杀结果?
- 发送消息失败,直接给 APP 返回秒杀失败结果;
- 发送消息成功,线程就阻塞等待秒杀结果同时设定一个等待的超时时间。
- 处理 APP 请求的线程需要同步等待秒杀结果,性能相对较低。
2、详解 RocketMQ 和 Kafka 的消息模型
假设有一个主题 MyTopic,我们为主题创建 5 个队列,分布到 2 个 Broker 中。
生产者:
生产者不用一一对应Broker或者队列,可以随便发送。每个生产者可以在 5 个队列中轮询发送,也可以随机选一个队列发送,或者只往某个队列发送。
消费端:
- 每个消费组就是一份订阅,它要消费主题 MyTopic 下,所有队列的全部消息。这里的“消费”,只是去队列里面读了消息,并没有删除,消费完这条消息还是在队列里面。
- 多个消费组在消费同一个主题时,消费组之间是互不影响的。
- 消费组的内部,一个消费组中可以包含多个消费者的实例。
- 由于消费确认机制的限制,原则是,在同一个消费组里面,每个队列只能被一个消费者实例占用。若某个消费者宕机,则重新分配。
消费位置:
- 每个消费组内部维护自己的一组消费位置,每个队列对应一个消费位置。
- 消费位置在服务端保存,并且,消费位置和消费者是没有关系的。
- 每个消费位置一般就是一个整数,记录这个消费组中,这个队列消费到哪个位置了,这个位置之前的消息都成功消费了,之后的消息都没有消费或者正在消费。
3、如何实现单个队列的并行消费?
- 若队列当前的消费位置是 5。同时来了三个消费者来拉消息,把编号为 5、6、7 的消息分别给三个消费者,每人一条。过了一段时间,三个消费成功的响应都回来了,可以把消费位置更新为 8 了,这样就实现并行消费。
- 若编号 5 的消息响应一直回不来,位置 5 就是一个消息空洞。为了避免位置 5 把这个队列卡住,可以先把消费位置 5 这条消息,复制到一个特殊重试队列中,然后把消费位置更新为 8,继续消费。再有消费者来拉消息的时候,优先把重试队列中的那条消息给消费者。
- 并行消费开销还是很大的,不应该作为一个常规的,提升消费并发的手段,如果消费慢需要增加消费者的并发数,还是需要扩容队列数。
4、如何保证消息的严格顺序?
全局严格顺序:把消息队列数配置成 1,生产者和消费者也只能是一个实例;
局部严格顺序:在发送端,使用账户 ID 作为 Key,采用一致性哈希算法计算出队列编号,指定队列来发送消息。一致性哈希算法可以保证,相同Key 的消息总是发送到同一个队列上,这样可以保证相同 Key 的消息是严格有序的。如果不考虑队列扩容,也可以用队列数量取模的简单方法来计算队列编号。