分布式事务、分布式锁(下)

最大努力通知


业务发起方将协调服务的消息发送到MQ,下游服务接收此消息,如果处理失败,将进行重试,重试N次后依然失败,将不进行重试,放弃处理,这个应用场景要求对事物性要求不高的地方。


分布式事务、分布式锁(下)


可靠消息最终一致(常用)

不要用本地的消息表了,直接基于MQ来实现事务。比如阿里的RocketMQ就支持消息事务。


分布式事务、分布式锁(下)


这个的意思,就是干脆不要用本地的消息表了,直接基于 MQ 来实现事务。比如阿里的 RocketMQ 就支持消息事务。


大概的意思就是:


A 系统先发送一个 prepared 消息到 mq,如果这个 prepared 消息发送失败那么就直接取消操作别执行了;


如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉 mq 发送确认消息,如果失败就告诉 mq 回滚消息;


如果发送了确认消息,那么此时 B 系统会接收到确认消息,然后执行本地的事务;


mq 会自动定时轮询所有 prepared 消息回调你的接口,问你,这个消息是不是本地事务处理失败了,所有没发送确认的消息,是继续重试还是回滚?一般来说这里你就可以查下数据库看之前本地事务是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,而确认消息却发送失败了。


这个方案里,要是系统 B 的事务失败了咋办?重试咯,自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如 B 系统本地回滚后,想办法通知系统 A 也回滚;或者是发送报警由人工来手工回滚和补偿。


这个还是比较合适的,目前国内互联网公司大都是这么玩儿的,要不你举用 RocketMQ 支持的,要不你就自己基于类似 ActiveMQ?RabbitMQ?自己封装一套类似的逻辑出来,总之思路就是这样子的。


分布式事务、分布式锁(下)


如果你真的被问到,可以这么说,我们某某特别严格的场景,用的是 TCC 来保证强一致性;然后其他的一些场景基于阿里的 RocketMQ 来实现分布式事务。


你找一个严格资金要求绝对不能错的场景,你可以说你是用的 TCC 方案;如果是一般的分布式事务场景,订单插入之后要调用库存服务更新库存,库存数据没有资金那么的敏感,可以用可靠消息最终一致性方案。


分布式锁的实现方案

一、基于数据库;

第一种实现方案,很简单,我们知道在传统数据库中是有ACID事务原子性、一致性、持久性、可用性规则的,如果基于数据库实现分布式锁,只需要在数据库中创建一个表,表中包含方法名,对方法名加上唯一索引,想要执行该方法时,就使用这个方法名向表中插入数据,插入时,其它数据都没法插入,等于获得锁,成功插入后,删除对应的数据释放锁。这种方案的好处就是简单,但存在的问题是对数据库要求高,因为数据库的可用性、性能会直接影响分布式锁的可用性,数据库可能需要主从部署、读写分离。


二、基于Redis;

,只需要使用redis的命令setnx、expire、delete就可以了(请允许我再感叹一下,redis真的太好用了,又简单性能又好),setnxkeyvalue就会给某个变量赋予一个值,返回1,当业务请求来时,如返回key值为1,线程获得锁,如果key值为0,线程抢锁失败。


三、基于Zookeeper;


zookeeper分布式锁原理

核心思想:当客户端要获取锁,则创建节点,使用完锁,则删除该节点


客户端获取锁时,在lock节点下创建临时顺序节点。

然后获取lock下面的所有子节点,客户端获取到所有的子节点之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁。使用完锁后,将该节点删除。


如果发现自己创建的节点并非lock所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,同时对其注册事件监听器,监听删除事件。


如果发现比自己小的那个节点被删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是lock子节点中序号最小的;如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。


上一篇:软件开发过程中常用的环境解释DEV FAT UAT PRO


下一篇:《用于物联网的Arduino项目开发:实用案例解析》—— 3.3 MQTT