文章目录
一、 什么是事务消息?
事务消息是投递消息的一种方式,可以确保业务执行成功,消息一定会投递成功。
需要在业务本地库创建一个消息表(t_msg)
create table if not exists t_msg
(
id varchar(32) not null primary key comment '消息id',
body_json text not null comment '消息体,json格式',
status smallint not null default 0 comment '消息状态,0:待投递到mq,1:投递成功,2:投递失败',
fail_msg text comment 'status=2 时,记录消息投递失败的原因',
fail_count int not null default 0 comment '已投递失败次数',
send_retry smallint not null default 1 comment '投递MQ失败了,是否还需要重试?1:是,0:否',
next_retry_time datetime comment '投递失败后,下次重试时间',
create_time datetime comment '创建时间',
update_time datetime comment '最近更新时间',
key idx_status (status)
) comment '本地消息表'
事务消息投递的过程
- step1:开启本地事务
- step2:执行本地业务
- step3:消息表t_msg写入记录,status为0(待投递到MQ)
- step4:提交本地事务
- step5:若事务提交成功,则投递消息到MQ,然后将t_msg中的status置为1(投递成功);本地事务失败的情况不用考虑,因为事务回滚了,此时消息记录也没有写到db的t_msg表中
异常情况
step5失败了,其他步骤都成功,此时业务执行成功,但是消息投递失败了,此时需要有个job来进行补偿,对于投递失败的消息进行重试。
消息投递补偿job
这个job负责从本地t_msg表中查询出状态为0记录或者失败需要重试的记录,然后进行重新投递到MQ。
对于投递失败的,采用衰减的方式进行重试,比如第1次失败了,则10秒后,继续重试,若还是失败,则再过20秒,再次重试,需要设置一个最大重试次数,最终还是投递失败,则需要告警+人工干预。
二、具体示例
用户下单后,我们需要通过MQ通知库存系统,库存系统扣减库存,如下:
上诉业务流程因为跨数据库,跨系统,所以很难直接保证事务性,此时就需要用到本地事务表了,如下:
说明:
- 用户下单后,不仅要插入记录到订单表,还需要插入一条消息记录到消息表,这两条记录因为是在同一个库,所以是可以直接用DB框架保证事务性的。
- 提交事务后,发送MQ,库存系统消费消息,更新库存记录,而后发送消息,订单系统消费到消息后,更新订单消息为处理完毕。
异常情况
- 订单系统发的消息失败了,或者发送成功后,但是丢失或者库存系统消费失败了
- 库存系统消费成功,但是发消息通知订单系统时失败了
无论是上面哪种情况,都会因为订单消息表的记录为正在处理,有定时任务扫描重发,因此会重试的,下游保证幂等即可。该方式可保证最终一致性。
上表解释:
- 消息编号:需要保证唯一性,比如雪花算法生成ID
- 订单编号:标识该消息是针对哪条订单的
- 消息内容:用于重试发送等
- 消息状态:跟踪消息处理进度,以及重试
- 创建时间:还应该有更新时间等
- 其余字段:重试次数,下次重试时间,是否还需要重试等
该方案所有可能的情况:
上面最后一条的 失败or
指的是网络错误等,比如一个订单消息由于网络原因,重试到最大次数都发不出去,一直失败,,或者当前时间距离消息创建时间达到指定阈值,还处于正在发送状态,此时我们应该根据具体的业务处理,将消息和订单都作废,或者作废并报警,然后走人工处理流程。