A brief discussion of message queues

Posted by Keal on February 7, 2021

MQ(消息队列)算是项目经常会用到的一个中间件了,之类对MQ做个大致认识

MQ的作用

解耦: 将同步的任务改为异步通知

广播: 将消息发给所有关心此条消息的服务

错峰流控: 通过MQ来限制一段时间的最大并发量

MQ工作流程

先看MQ的基本工作流程,简化为生产者, MQ, 消费者的关系

producer->MQ->consumer->MQ->producer

MQ负责接收来自producer的消息,然后根据consumer对消息的处理来维护消息的状态.

MQ问题

可靠性和性能的矛盾.

producer发送消息到MQ,如果在consumer->MQ->producer中间处了问题.比如: 网络突然故障, 机器宕机, 机器消费了消息但是没回复MQ, MQ的消息存储没有持久化,还没被消费就挂掉,重启没能恢复等等, 碰到这种情况,如果要保证可靠性,就要有重发机制.重发就有可能导致消息的重复消费.如果不需要保证可靠性,那么就要接受消息丢失的情况.

目前大部分的业务场景,是无法接受一个消息丢失的,那么就要接受消息重发. 对于消息重发的处理,简单来说是要做到:

  1. producer和MQ应该减少重复消息的发送 MQ能够对每条消息做一个唯一性判断,丢弃重复的消息
  2. consumer应该能够幂等的处理重复的消息 a. 消息内容中包含一个唯一的ID,消费者通过此ID来鉴重,例如 i.使用数据库的唯一键 ii.bloom_filter(布隆过滤器) iii.基于时间戳或其他生成的唯一MessageId

还有一种情况,那就是消息乱序.举个例子就是比如一个开关的”开”,”关”指令,生产者按”开”,”关”的顺序发送.但是consumer接到的顺序是”关”,”开”.如果不管消息乱序的状况,那么最终的结果就错了.想要保证在消息乱序情况下consumer处理的幂等性,有两种可行的方法:

  1. 借鉴数据库的mvcc思想,给每个操作带一个版本号,通过版本号之间的关系来决定是否处理.
  2. 状态机,根据当前状态处理,不能处理的打回重发.限制最大次数.达到最终业务正确.

Push和Pull模型

Push模型就是MQ主动推送消息到consumer

Pull模型就是consumer自己去MQ拉取消息

Push模型:

优点:

能够及时使消息得到消费.—–及时性很强

缺点:

需要维护各个worker的状态,否则可能会造成在业务繁忙时,worker处理不过来又发回到MQ等待重发.来回踢皮球造成消息堆积.

Pull模型:

优点:

可以根据自己的消费能力来从MQ中获取消息,这样甚至可以将多个消息合并在一起处理

缺点:

可能会造成消息消费的延迟.因为当消息的发布不够均匀时,worker多久去取一次消息会是一个问题.

Q&A

Q:消息队列如何保证消息按顺序消费

A:将顺序消息保存在消费队列的同一个messageQueue中,因为同一个queue能保证消息的顺序发放. 同时只有一个consumer来消费这个messageQueue避免并行下的顺序错乱问题.

Q:消息队列的缺点

A:系统的可用性降低,多了对消息队列的依赖,消息队列服务如果故障,整个服务也会故障 一致性的问题处理.

Reference

InfoQ-如何保证消息队列的正确性

美团-消息队列设计概要