分布式常问问题

分布式系统

zk使用场景

  1. 配置信息管理。类似dubbo的注册中心,有provider路由信息等。
  2. 分布式锁。
  3. HA。主备服务都在zk中有,主服务挂了zk有感知,然后切换到备用服务。

分布式锁

1. redis实现分布式锁

1.1 基础实现

主要就是加锁和解锁两个操作。

加锁:

set(key 随机值 NX PX 30000)
    //key 锁的key
    //随机值 锁的value,加锁的对象,也就是是哪个客户端加的锁(解锁的时候只能由加锁的客户端去解锁)
	//NX: 当key不存在的时候,set一个key;当key存在的时候不操作(判断锁是否被客户端持有)
	//PX: 锁的超时时间

对于两个分布式服务去竞争锁,服务A通过上面的set加锁操作获取到了锁,也就创建了一个key;服务B执行set操作的时候发现这个key已经有了,那么就没有创建成功,这时,会每隔一段时间去再次执行这个加锁操作。此外,因为设置过期时间,那么过期时间之后这把锁还能被别的服务获取到。

解锁:

解锁的时候主要就是删除key就可以了,但需要注意的是只有加锁对象才能删除这个key,也就是需要之前set的value也相等才能删除,一般用lua脚本去删除。

问题:

  1. 获取被占用的锁需要每隔一段时间去尝试获取,消耗性能。
    1. 如果redis挂了,那么整个分布式锁就不能用了,可用性不高。

1.2 RedLock实现

这个方法就是上面这个的集群版本,能改善单个redis的不可用情况,但是实现需要整个集群比较麻烦。

2. zk实现分布式锁

2.1 基本实现

zk实现分布式锁,主要是建立一个临时节点,如果建立成功了就说明获取到了锁;这个时候别的客户端来创建锁会失败,只能注册个监听器监听这个锁。释放锁就是删除这个znode,一旦释放掉就会通知客户端,然后有一个等待着的客户端就可以再次重新加锁。

此外,zk中有永久节点和临时节点等,临时节点会在客户端跟zk的会话结束的时候自动删除,这样就避免了死锁。

2.2 临时顺序节点

临时顺序节点是多个竞争锁的客户端会每个建立临时顺序节点,最小的节点会获取到锁,然后后面的节点依次监听前一个节点,这样就能顺序依次让后面客户端获取到锁。

这种方法的好处在于前一个方案,当释放锁删除临时节点的时候,会唤醒所有等待的监听器,但最终只会让其中一个再次获取到锁,效率不高;而这个方案每个监听器只监听之前的节点。

3. redis和zk实现分布式锁对比

在实现的健壮性和逻辑性上,zk实现都由于redis的实现;并且redis每隔一段时间去获取锁的方式没有监听获取锁的方式好,性能消耗大。

分布式session

实现的核心是将放入session中的数据放到redis或数据库中,这样不同的服务节点要取session中数据时都去redis中取值。

1. tomcat+redis

在tomcat配置文件中配置RedisSessionManager

2. spring session+redis

分布式事务

1. XA协议/二阶段提交

主要包括事务协调者和事务参与者两种角色。

第一阶段:事务协调者会给所有事务参与者发送prepare(准备)请求,事务参与者接收到prepare请求之后对进行数据库操作,操作完成之后并不直接提交事务,而是给事务协调者发送done或fail消息,当事务协调者接受到了所有事务参与者返回的消息之后进入第二阶段。

第二阶段:如果事务协调者收到的返回消息都是done,就表明所有操作都成功,事务协调者会给所有事务参与者发送commit消息,事务参与者接收到commit之后就可以进行事务提交,并且在完成事务提交之后给事务协调者发送ACK消息,表明事务提交完成,当事务协调者接受到了所有事务参与者发送的ACK消息之后整个事务结束完成;如果事务协调者收到的返回消息有至少一个fail,事务协调者会给所有事务参与者发送abort,事务参与者收到abort会进行事务回滚。

特点

  1. 遵循强一致性,事务协调者需要等待所有事务参与者返回消息或给所有事务参与者发送消息,性能上不好。
  2. 可能commit消息丢失,造成数据不一致。
  3. 一般XA协议这种使用的场景是系统操作多个分库的数据库,但是这种直接操作多个分库数据库的方案,特别对于微服务架构的系统来说,会造成服务治理变得异常混乱,与微服务架构一个服务操作一个数据库的设计规范相违背。

2. TCC 补偿事务

TCC补偿事务分为三个步骤:try,confirm,cancel。

try阶段:尝试执行业务,进行检查并“冻住”数据。

confirm阶段:真正执行业务。

cancel阶段:可能进行回滚,释放try阶段冻住数据。

特点

方案不具备通用性,需要开发者自己实现,适用于最核心的业务。

3. 本地消息表

本地消息表是将分布式事务分为了多个本地事务去执行的。首先在消息生产者数据库中增加一个消息表,通过事务将消息表和业务操作管理起来,进行了业务操作同时也向消息表写入了一条消息;通过轮询等方式将消息表中的消息通过MQ写给消息消费者,这样就解决了消息通知的问题;对于消息消费者也增设一张消息表,并有消息的状态属性,通过事务将消息消费业务处理和消息表状态更新进行管理,解决了重复消费的问题。

特点

  1. 这个方案保证了数据的强一致性,即使出现问题,消息生产者也会通过不断轮询的方式试图将消息发送出去,直到发送成功。
  2. 这个方案需要在数据库中增加一个消息表,分布式事务的管理建立在这个消息表上的,但这种方案需要和数据库频繁交互,在高并发场景并不可用。

4. 可靠消息最终一致性

1)A系统先发送一个prepared消息到mq(recketmq支持这种事务),如果这个prepared消息发送失败那么就直接取消操作别执行了

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

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值