写在前面
作者一直想深入的去研究一款消息队列MQ,例如 延迟消费,广播消息,失败重试等机制.
奈何市面上的MQ中间件太多了,如kafka,activeMQ,rocketMQ....所以也一直在纠结该以哪款为入手点,对新人友好?
想起作者曾深入的研究了xuxueli大佬的分布式调度系统(XXL-JOB),然后进入XXL社区,刚好发现了的另一个开源项目,即分布式消息队列(XXL-MQ).
简而言之四个字,以小见大,懂得都懂
何为顺序性消费?
这个词经常出现在面试环节中,这里描述一下;
例如你往消息队列依次顺序地发送了以下消息
insert into student values (1,"张三") //增加一条数据
update student name="张三名字修改" where id=1 //修改这条数据
delete from student where id=1 //删除这条数据
那么对于消费端来说,无论是集群还是单机部署,你希望看到的消息消费顺序也一定是这样
insert into student values (1,"张三") //增加一条数据
update student name="张三名字修改" where id=1 //修改这条数据
delete from student where id=1 //删除这条数据
如果你的消费端消费顺序变成了
delete from student where id=1 //删除这条数据
update student name="张三名字修改" where id=1 //修改这条数据
insert into student values (1,"张三") //增加一条数据
或者是
update student name="张三名字修改" where id=1 //修改这条数据
delete from student where id=1 //删除这条数据
insert into student values (1,"张三") //增加一条数据
你会发现你的数据库多了一条脏数据,然后就等着卷铺盖滚蛋了
非顺序性消费 场景复现
这里给大家真实代码展示一下,非顺序性消费的情况
如图: 我部署了三个消费者集群 8005,8006,8007
然后模拟各个消费端的消费所用的不同时间
并且也是输出了当前消费者消费此条消息的时间
// 模拟网络卡顿等因素
Thread.sleep((int) (Math.random() * 20000));
然后我们往topic=topic_1 依次发送三条消息,
顺序性消费No:0
顺序性消费No:1
顺序性消费No:2
然后我们观察下8005,8006,8007三个消费端的消费情况
整理一下
8006 ,消费一条消息:顺序性消费No:1,消费时间Mon Feb 28 15:35:31 CST 2022
8005 ,消费一条消息:顺序性消费No:2,消费时间Mon Feb 28 15:35:41 CST 2022
8007 ,消费一条消息:顺序性消费No:0,消费时间Mon Feb 28 15:35:54 CST 2022
这里就发现了 消费顺序变成了顺序性消费No:1,顺序性消费No:2,顺序性消费No:0 即非顺序性
解决方案
-
单机部署的消费端
这种直接无需操作,自身即是顺序性,因为消息队列本身的特性就是先进先出,有顺序性的,单机部署消费去消息队列中心拉取消息时,也是按照顺序来的,如图(还是上面的例子)
-
集群部署的消费端
数据库乐观锁形式 给每条消息增加扩展字段consumer_status,用于记录,维护每一条消息的消费状态 如有以下顺序消息 insert into student values (1,"张三") //增加一条数据 update student name="张三名字修改" where id=1 //修改这条数据 delete from student where id=1 //删除这条数据 当 8006消费者2准备MQ中心拉取消息 update student name="张三名字修改" where id=1 //修改这条数据 先查看前一条消息insert into student values (1,"张三") //增加一条数据 的consumer_status字段是否为 已消费结束/消费成功,如果是则拉取成功,并将这条消息字段更新为消费中,当8006消费者2 完成消息消费时,再将consumer_status字段更新成已消费结束/消费成功 这期间对于8007消费者3来说,是一直被阻塞的,当8006完成消费后,8007才能继续消费,从而维持顺序性 方案弊端: 1 如果有10条顺序性消息,那么第一条消息没有被消费完成时,则会一直阻塞后续消息的消费,造成网络拥堵 2 消息还有个失败重试机制,即消息被消费失败后,可以有几次重试消费机会,如何去合理管理消息失败重试阶段与消息消费状态consumer_status的维护,这个二次成本应该有点大 如何优化? 做成topic 业务可配置选项,例如在银行,支付等业务中,这种顺序性消费就如同刚性分布式事务一样,和💰💰💰有关,因此这种业务一定要开启此配置 其他的例如网络群聊,广告推送这种要求不严的业务,则不开启此配置