(一)跨两个dubbo服务,分布式事务解决方案
例如:
会员完成注册后,需要为会员发放奖励(派发一张优惠券),会员相关服务在mod-member模块,优惠券相关服务在mod-card模块,所以说传统的实现方案(在controller层分别调用两个dubbo服务)就会存在数据不一致问题,controller按照顺序先调用mod-member中的注册接口,然后调用mod-card中的派券接口,这时候会有两种可能
I)注册异常,mod-member自己回滚,程序终止,不会出现数据不一致
II)注册成功,派券异常,mod-card自己回滚,此时mod-member已经提交,并且和mod-card不在同一个TransactionManager中,没法回滚,导致注册成功,用户派券失败
解决方案:
业务下沉,将逻辑从controller层下沉到mod-member中,mod-member中优先调用本地服务注册会员,调用dubbo服务派发优惠券,程序运行有以下异常可能
I)本地服务异常(会员注册失败),事务发生回滚,程序终止,不再调用dubbo派券服务
II)本地服务正常(会员注册成功),dubbo派券服务异常<? Extends RuntimeException>,此时dubbo服务自己的事务管理器回滚派券事务,mod-member捕获到dubbo异常,本地事务管理器将本地事务(会员注册)回滚
这两种异常,都能保证数据一致性
(二)跨三个及以上的dubbo服务,分布式解决方案
例如:
会员注册(mod-member)完成后,需要派发优惠券(mod-card),并且需要记录当前商户下新增会员数量(mod-shop),此时就涉及到跨三个dubbo服务的操作,用传统的controller顺序调用和(一)中的方案都无法解决分布式事务数据一致性问题。
解决方案:
业务下沉,逻辑合并,类似(一)中简单的将业务下沉到mod-member中无法解决,可以分析逻辑调用关系以及模块之间依赖后(dubbo服务依赖做到和maven一致,不循环依赖),
Controller层调用mod-member,mod-member模块调用本地服务注册,和mod-card,
Mod-card模块调用本地服务派券和mod-shop服务记录会员增量。这样mod-card模块能保证本模块和mod-shop模块数据一致性,mod-member能保证本模块和mod-card模块数据一致性(比如说,注册和派券都成功,mod-shop记录会员增量异常,自己回滚,向mod-card抛RuntimeException,然后mod-card捕获RuntimeException,派券回滚,向mod-member抛RuntimeException,最后mod-member捕获异常,会员注册回滚,响应给controller层错误)
*注
Atomikos和Jtom无法解决dubbo(x)分布式服务中的事务一致性问题,因为其提供了一种集中式的基于数据源的事务管理。举例说明:zhenai-crm-financial中有牵扯到zhenai-crm-member和zhenai-crm-invite的更新操作,如果要保证数据一致性,需要在zhenai-crm-financial中配置三个库的数据源(dataSource),交给atomikos事务管理器统一管理;这样的话,每个模块有牵扯到其他模块的更新操作都需要配置多个数据源,比较繁杂;另外也完全背离了dubbo(x)分布式服务的初衷(各个业务模块之间屏蔽业务和数据库操作,只提供接口服务调用)。
对于弱一致性的业务可以考虑主业务完成后发一个广播或者私有队列消息出来,由其他模块消费处理(异步,可接受延时,但是要处理消息幂等问题)
本文分享自 PersistentCoder 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!