微服务中分布式事物的重要性
真实案列
刚刚入职新公司时,还记得当时上个产品生产环境报了一个bug。
场景
项目是微服务架构,App业务服务/积分服务。
问题
: 用户注册送积分,偶现 用户注册成功,可积分未赠送
。
查找问题过程
: jmeter 压力测试,抓日志,日志如下:
App服务日志
:
App服务
:注册日志,从日志查看可明显看出执行 insertAppUserSign
方法时间是 2018-07-11 15:39:20.455
积分服务日志
:
App服务
:注册日志,从日志查看可明显看出执行 getAppUserByIds
方法时间是 2018-07-11 15:39:20.453
积分服务
:判断用户是否存在。
//查询用户是否存在
List appUserIds = Lists.newArrayList();
appUserIds.add(requestDto.getAppUserId());
List appUserByIds = integralRewardMapper.getAppUserByIds(appUserIds);
if (appUserByIds == null || appUserByIds.isEmpty()){
throw new RestException(ReturnCodeEnum.INVALID.code,ReturnCodeEnum.INVALID.msg);
}
问题总结
:具体原因可看出,因积分服务
在为用户新增积分时,会判断用户是否存在
。可从日志看App服务的用户在20.455s
才入库,而积分服务在20.453s
就已经在查询该用户是否存在了,那么该用户注册应该得到的积分确没有
。这就是在多服务情况下容易出现的问题,以及非常经典订单与库存案例:订单下单了,库存仍然有,则会出现一个商品可能会被下单两次【秒杀问题】
。
利用MQ实现分布式事物
这里使用的RabbitMq,为什么使用Rabbit呢?还有Kafka,ActiveMq,RocketMq为什么不选择呢?
这里就简单说一下:kafka
主要业务为处理日志这块,利用elk可以更好的处理庞大的日志信息。
RocketMq
这是阿里研发的消息队列,当时还没有不知道,见笑了。ActiveMq
相对于RabbitMq
开源社区的活跃度不高。
利用RabbitMq (画图工具https://www.draw.io/)
- 1、事物发起方首先发送消息到MQ
- 2、本地业务处理
- 3、业务处理成功,将成功表示存入redis,业务处理失败,则跑出异常,事物回滚
- 4、执行本地事物
- 5、MQ消费端消费消息【
需判断标识是否存在,如果不存在则说明业务需回滚,存在则进行消费
】
利用Rocket 4.3版本 支持分布式事物 (画图工具https://www.draw.io/)
1、事务发起方首先发送 prepare(即发送Half消息) 消息到 MQ。
2、在发送 prepare 消息成功后(即收到Half消息发送成功的回执消息) ,执行本地事务(业务系统自己的本地事物逻辑代码)。
3、根据本地事务执行结果,返回给MQ发送方发送: commit 或者是 rollback。
4、如果MQ发送方接收到的消息是: rollback,MQ 将删除该 prepare 消息不进行下发,如果是 commit 消息,MQ 将会把这个消息发送给 consumer 端。
5、如果执行本地事务过程中,执行端挂掉,或者超时,MQ 将会不停的询问其同组的其他 producer 来获取状态。
6、Consumer 端的消费成功机制有 MQ 保证。
参考
https://mp.weixin.qq.com/s/43wwC4lp77m4foVPEgTRlA
不停的发现,不停的学习