目录
消息为什么要顺序消费
一个订单会有很多种状态:待支付、已支付、待发货、交易完成等等。
每一种状态的流转都依赖上一个状态,其中一些状态是不可逆的,例如不能从已支付退回待支付。
订单状态流转就是一个典型的状态机场景,如图所示。
订单基于状态机设计模式可以很方便实现乐观锁,在分布式环境,可能多个实例同时将订单从一个状态更为为另一个状态,基于MySql的MVCC多版本并发控制,只有一个线程能更改成功。基于状态机的乐观锁保证了在微服务环境下的线程安全。
UPDATE order SET status = '已支付' WHERE status = '待支付' AND order_id = 10001
UPDATE order SET status = '已发货' WHERE status = '已支付' AND order_id = 10001
update语句涉及对数据库的更改,MySql会按顺序产生对应的binlog,可以基于Alibaba开源中间件Canal订阅binglog,实现订单消息的准实时收集,例如推送给ElasticSearch。订阅binlog一大好处就是业务逻辑不需要关心 ES 数据的写入。在大批量下单,活动、秒杀场景可能有大批量的binlog变更,如果直接通过Canal订阅binlog变动,会造成Canal Client瞬间爆掉。为了解决这个问题,我们可以引入kafka做一层封装,起到削峰作用。消息是异步的,基于消息投递的更新和删除的操作,如果出现了先发后至/后发先至的情况,就会导致ES里的数据是比较旧的数据,如果待支付消息覆盖了已支付消息是不允许的。
消息顺序投递方案
Canal就是把自己伪装成了MySQL的一个slave,然后同步其binlog。
具体使用方法和原理可以查看github主页 https://github.com/alibaba/canal
Canal可以把有序的binlog按原来顺序投递到kafka,而Kafka也支持消息按投递顺序消费。
Alibaba Canal对集群的支持不太友好,除了Canal,kafka也有丰富的connector支持将数据导入kafka,一样支持顺序投递,而且原生支持集群的方式。
kafka顺序投递
这里不多讲kafka分区与消费者之间的关系,只需要了解Kafka 可以保证分区消息的顺序。
如果使用同一个生产者往同一个分区写入消息,而且