截拳道,指的是不拘于形式,思想上成熟的觉悟,以水为本质而攻击,反击;将一切化解于无形。截拳道的最大特点,是注重于“生活上实际的运用”,抛弃了传统武术复杂的形式套路。
相关文章:
- 分布式事务之 Atomikos
- 分布式事务之再理解 TCC 及相关实现框架的源码分析(一)
- 分布式事务之 Spring 编程式事务
之前的几篇文章中已经介绍了分布式事务中的几种方案,本文介绍另外一种经典的方案:基于”消息“的最终一致性方案。既然是基于”消息“,那么一般来说就是异步处理的,就必然会存在”时延“的问题,所以选用这种方案的话要可以接收它存在的缺点。
这种基于”消息“的分布式事务解决方案,综合很多资料来看,也分为两类:1.基于可靠消息;2.最大努力通知。
先说最大努力通知,也有说法叫本地消息表,网上有各种各样的流程图。但我个人觉得没必要拘泥于所谓的流程图,因为我们的目标是解决业务问题,而不是去生搬硬套所谓的流程图。
最大努力通知,或者说本地消息表一个最重要的点就是通过一种重试的机制去执行消息,但是他不会完美的保证消息的成功发送。
再以分布式事务之再理解 TCC 及相关实现框架的源码分析(一)中的例子为例:
1.用户下单(inst1)
1.1.生成订单(inst1 本地事务)
1.2.用户京豆+线上支付+增加积分(inst1 RPC inst2)
1.2.1.扣款(inst2 RPC inst3)
1.2.2.扣减京豆(inst2 RPC inst4)
1.2.2.增加积分(inst2 RPC inst5)
1.3.扣减库存(inst1 RPC inst6)
1.4.发送下单成功短信(inst1 RPC inst7)
这里不纠结 1.2~1.3 的分布式事务的实现方式(或者假设使用的 TCC),只关心第 1.4 步”发送短信“,我们其实有很多选择,比如直接发送给 MQ,基于 MQ 的重试+ ACK 机制,直到下游短信服务执行成功了,那么”1.用户下单“这个逻辑上的分布式事务才算是执行完成了。
其实这就是所谓的最大努力通知方案的流程,它就是有一个重试机制,可以基于重试机制去保证消息的成功消费。但是细究起来,还是有很多细节的。
现在问题来了,对于 inst1 来说,它是整个分布式事务的发起方,1.1.生成订单是 inst1 的本地事务的执行,那么1.4.发送短信需要与 1.1 的本地事务放在一个本地事务(@Transactional)里吗?
如果放在一个本地事务里,万一发送短信发到 MQ 的时候失败了,订单流程就要回滚,难道仅仅因为发短信失败了,整个订单流程就要回滚吗?这个回滚的代价是很大的。再或者万一短信发成功了,这时候服务宕机了,订单没有生成,但是短信却发出去了,这样也不合适,也出现了数据不一致(在最大努力通知方案中也还有一种校对的思想,就是说下游服务可以去向上游服务去进行数据校对,这种比如短信发送服务发短信前向上游服务进行校对,判断订单是否生成成功等)。
但是如果不放在一个本地事务里面,万一本地执行成功了,订单啥的都生成了,这时候服务宕机了,但是短信却没发送出去,也造成了数据不一致。
于是就可以这样,我把要发送的短信存储在本地数据库中,也就是说 1.1 和 1.4 都变成了本地事务,然后再搞一个定时任务去扫描这些”短信“数据,异步发送到 MQ。这个方案要说缺点的话,也有,比如增加了对数据库的读写(个人觉得几乎可以忽略),通用性不太好。
其实这里提到的”要发送的短信存储在本地数据库中“,这里就是一个”本地消息表“。
至于通用性的问题,其实可以这样,搞一个通用本地消息服务(或者称之为最大努力通知服务),它有一个本地消息表,负责接收从 MQ 来的分布式事务消息,然后它通过定时任务去异步执行分布式消息,也就是大致流程为:
1.事务发起方执行执行本地事务并且提交(与2.发送MQ消息是否放在一个事务及相关分析见上文); 2.发送MQ消息; 3.通用本地消息服务接收MQ消息,存入本地消息表(定时任务);
还有个问题,下游服务一直消费失败怎么办呢,比如可以达到一定次数后进行人工干预或者干脆就不管了(根据业务来)。
总得来说,最大努力通知最主要思想就是基于重试去确保消息的执行成功,但是其中也有很多细节,根据业务需要再去进行相应的处理。
最后,还是想表达文章开头的”截拳道“的思想,不要拘泥于形式(但是不拘泥于形式的前提是得了解先有的招式),真正的去找到适合当前业务的解决方案。
References- https://baike.baidu.com/item/%E6%88%AA%E6%8B%B3%E9%81%93/30401
- https://zh.wikipedia.org/wiki/%E6%88%AA%E6%8B%B3%E9%81%93
欢迎关注公众号:



