../

MQ常见知识点

一、为什么要使用消息队列?

1.1 异步

1.2 解耦

1.3 消峰

二、 使用消息队列带来的问题

2.1 消息积压

消费者消费满或者消费者干脆挂掉了。

2.2 系统雪崩

MQ挂了,整个系统跟着挂,MQ一旦出现故障,系统不能往MQ发送消息,也不能从MQ消费消息,整个系统就崩溃了。

2.3 消息消费顺序性

2.4 消息丢失

2.5 消息幂等(消息重复消费)

  1. 版本号去重

举个简单的例子,一个产品的状态有上线/下线状态。如果消息1是下线,消息2是上线。不巧消息1判重失败,被投递了两次,且第二次发生在2之后,如果不做重复性判断,显然最终状态是错误的。 但是,如果每个消息自带一个版本号。上游发送的时候,标记消息1版本号是1,消息2版本号是2。如果再发送下线消息,则版本号标记为3。下游对于每次消息的处理,同时维护一个版本号。 每次只接受比当前版本号大的消息。初始版本为0,当消息1到达时,将版本号更新为1。消息2到来时,因为版本号>1.可以接收,同时更新版本号为2.当另一条下线消息到来时,如果版本号是3.则是真实的下线消息。如果是1,则是重复投递的消息。 如果业务方只关心消息重复不重复,那么问题就已经解决了。但很多时候另一个头疼的问题来了,就是消息顺序如果和想象的顺序不一致。比如应该的顺序是12,到来的顺序是21。则最后会发生状态错误。 参考TCP/IP协议,如果想让乱序的消息最后能够正确的被组织,那么就应该只接收比当前版本号大一的消息。并且在一个session周期内要一直保存各个消息的版本号。 如果到来的顺序是21,则先把2存起来,待1到来后,先处理1,再处理2,这样重复性和顺序性要求就都达到了。

  1. 数据库唯一索引
  2. 状态机

2.6 事务问题

消息一致性需要得到保障。最终一致性,主要是用“记录”和“补偿”的方式。在做所有的不确定的事情之前,先把事情记录下来,然后去做不确定的事情,结果可能是:成功、失败或是不确定,“不确定”(例如超时等)可以等价为失败。成功就可以把记录的东西清理掉了,对于失败和不确定,可以依靠定时任务等方式把所有失败的事情重新搞一遍,直到成功为止。

以本地和业务在一个数据库实例中建表为例子,与扣钱的业务操作同一个事务里,将消息插入本地数据库。如果消息入库失败,则业务回滚;如果消息入库成功,事务提交。 然后发送消息(注意这里可以实时发送,不需要等定时任务检出,以提高消息实时性)。以后的问题就是最终一致性问题了,只要消息没有发送成功,就一直靠定时任务重试。 这里有一个关键的点,本地事务做的,是业务落地和消息落地的事务,而不是业务落地和RPC成功的事务。这里很多人容易混淆,如果是后者,无疑是事务嵌套RPC,是大忌,会有长事务死锁等各种风险。 而消息只要成功落地,很大程度上就没有丢失的风险(磁盘物理损坏除外)。而消息只要投递到服务端确认后本地才做删除,就完成了producer->broker的可靠投递,并且当消息存储异常时,业务也是可以回滚的。

四、常见MQ的特点

4.1 Pull or Push模型

观察者模式、推;Future模式、拉

ActiveMQ是JMS的实现,JMS 消费消息后,会删除。JMS 消息种类分的细;Kafka 的消息没有做详细区分,Kafka消息会写入磁盘,所以一般写入消息较大,客户端可以定制消费。所以是客户端主动去pull消息。