RocketMQ解决消息发送与业务落库的一致性
2021-04-21 18:07:00 745
在实际业务中, 经常有如下场景
更新一条数据后, 需要发送一条消息异步通知其他服务进行后续处理
比如用户完成激活, 发送一条消息通知第三方服务同步用户状态
那么会有如下代码
@Transactional
public void enabled() {
//更新用户状态
updateUser();
//发送消息
sendMsg();
}
这样写可能出现这样的问题
事务提交失败, 消息发送成功
那换种方式写
将sendMsg();
放在事务外面, 等事务提交成功之后才发送消息
这样也会有问题, 事务提交成功, 但消息发送失败
这两种情况都会导致业务数据出现异常
使用RocketMQ的事务消息就可以解决这个问题.
- 我们在数据库事务中先发送一个半消息, 这个半消息此时是不能被消费的. 待RocketMQ返回Ack后, 再执行本地事务
- 然后数据库事务结束之后, 再确认数据库事务状态, 确认执行成功了再向RocketMQ发送确认消息, 此时的消息才能算完全体, 可以被Consumer进行消费.
- Broker会定时扫描长时间没有进行二次确认的消息, 主动向Producer进行消息回查. 以确定消息是否能正常消费
实现最终一致性
整体机制有点类似MySQL/InnoDB undolog/redolog的二阶段提交
- redolog先prepare(innodb_flush_log_at_trx_commit设置成1的情况下, 立即刷盘一次) 该阶段若crash则事务回滚
- binlog再写(sync_binlog这个参数设置成1的时候, 每提交1次事务, 都立即持久化到磁盘)立即刷盘 该阶段若crash则同样事务回滚
- mysql server调用引擎的提交事务接口, redolog 改为commit状态. 该阶段若crash, mysql恢复后会判断binlog中是否已有记录, 若有, 则最终提交事务, 若没有, 则回滚事务