1、什么是幂等?如何解决幂等性问题?

幂等性的核心思想,其实就是保证这个接口的执行结果只影响一次,后续即便再次调用,也不能对数据产生影响,之所以要考虑到幂等性问题,是因为在网络通信中,存在两种行为可能会导致接口被重复执行。

  1. 用户的重复提交或者用户的恶意攻击,导致这个请求会被多次重复执行。
  2. 在分布式架构中,为了避免网络通信导致的数据丢失,在服务之间进行通信的时候都会设计超时重试的机制,而这种机制有可能导致服务端接口被重复调用。

所以在程序设计中,对于数据变更类操作的接口,需要保证接口的幂等性。

  1. 使用redis里面提供的setNX指令,比如对于MQ消费的场景,为了避免MQ重复消费导致数据多次被修改的问题,可以在接受到MQ的消息时,把这个消息通过setNx写入到redis里面,一旦这个消息被消费过,就不会再次消费。
  2. 建去重表,将业务中由唯一标识的字段保存到去重表,如果表中存在,则表示已经处理过了
  3. 版本控制,增加版本号,当版本号符合时候,才更新数据
  4. 状态控制,例如订单有状态已支付 未支付 支付中 支付失败 当处于未支付的时候才允许修改成支付中

举个栗子:比如添加请求的表单里,在打开添加表单页面的时候,就生成一个AddId标识,这个AddId跟着表单一起提交到后台接口。

后台接口根据这个AddId,服务端就可以进行缓存标记并进行过滤,缓存值可以是AddId作为缓存key,返回内容作为缓存Value,这样即使添加按钮被多次点下也可以识别出来。

这个AddId什么时候更新呢?只有在保存成功并且清空表单之后,才变更这个AddId标识,从而实现新数据的表单提交

2、请说一下你对分布式锁的理解,以及分布式锁的实现

分布式锁,是一种跨进程的跨机器节点的互斥锁,它可以用来保证多机器节点对于共享资源访问的排他性。我觉得分布式锁和线程锁本质上是一样的,线程锁的生命周期是单进程多线程,分布式锁的声明周期是多进程多机器节点。

在本质上,他们都需要满足锁的几个重要特性:

  • 排他性,也就是说,同一时刻只能有一个节点去访问共享资源。
  • 可重入性,允许一个已经获得锁的进程,在没有释放锁之前再次重新获得锁。
  • 锁的获取、释放的方法
  • 锁的失效机制,避免死锁的问题

1、关系型数据库,可以使用唯一约束来实现锁的排他性,那抢占锁的逻辑就是:往表里面插入一条数据,如果已经有其他的线程获得了某个方法的锁,那这个时候插入数据会失败,从而保证了互斥性。

2.Redis,它里面提供了SETNX命令可以实现锁的排他性,当key不存在就返回1,存在就返回0。然后还可以用expire命令设置锁的失效时间,从而避免死锁问题。

当然有可能存在锁过期了,但是业务逻辑还没执行完的情况。 所以这种情况,可以写一个定时任务对指定的key进行续期。Redisson这个开源组件,就提供了分布式锁的封装实现,并且也内置了一个Watch Dog机制来对key做续期。Redis是一个AP模型,所以在集群架构下由于数据的一致性问题导致极端情况下出现多个线程抢占到锁的情况很难避免

3.zookeeper分布式锁。zk通过临时节点,解决了死锁的问题,一旦客户端获得锁之后突然挂掉,那么这个临时节点就会自动删除掉,其它客户端自动获得锁,临时顺序节点解决了惊群效应

对于Redis看门狗机制

1、如果我们指定了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是我们制定的时间,不会自动续期;
2、如果我们未指定锁的超时时间,就使用 lockWatchdogTimeout = 30 * 1000 【看门狗默认时间】

只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔10秒都会自动再续成30秒;
 

@GetMapping(value = "/hello")
@ResponseBody
public String hello() {
    //1、获取一把锁,只要锁的名字一样,就是同一把锁
    RLock lock = redisson.getLock("my-lock");
    //2、加锁 默认加锁时间30s
    lock.lock(); 
    try {
        System.out.println("加锁成功,执行业务..."  + Thread.currentThread().getId());
        TimeUnit.SECONDS.sleep(20);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        //3、解锁
        System.out.println("释放锁..." + Thread.currentThread().getId());
        lock.unlock();
    }

    return "hello";
}

3、分布式事务解决方案

在分布式系统下,一个业务跨越多个服务或数据源,每个服务都是一个分支事务,要保证所有分支事务最终状态一致,这样的事务就是分布式事务。

2.分布式事务本质上要解决的就是跨网络节点的多个事务的数据一致性问题,业内常见的解决方法有两种

(1)强一致性,就是所有的事务参与者要么全部成功,要么全部失败,全局事务协调者需要知道每个事务参与者的执行状态,再根据状态来决定数据的提交或者回滚!

(2)最终一致性,也叫弱一致性,也就是多个网络节点的数据允许出现不一致的情况,但是在最终的某个时间点会达成数据一致。基于CAP定理我们可以知道,强一致性方案对于应用的性能和可用性会有影响,所以对于数据一致性要求不高的场景,就会采用最终一致性算法。

3.在分布式事务的实现上,对于强一致性,我们可以通过基于XA协议下的二阶段提交来实现,对于弱一致性,可以基于TCC事务模型、可靠性消息模型等方案来实现。

市面上有很多针对这些理论模型实现的分布式事务框架,我们可以在应用中集成这些框架来实现分布式事务:比如Seata等

4、说说CAP理论、BASE理论、AP模式和CP模式

Consistency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致

Availability (可用性):用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝

Partition(分区)Tolerance(容错):因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区,在集群出现分区时,整个系统也要持续对外提供服务

cp和ap :分区容错是必须保证的,当发生网络分区的时候,如果要继续服务,要么强一致性和可用性,只能2选1

BASE理论是对CAP的一种解决思路,包含三个思想:

Basically Available(基本可用)分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。

Soft State(软状态):在一定时间内,允许出现中间状态,比如临时的不一致状态。

Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。

AP模式:各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致。

CP模式:各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态。

5、分布式id生成方案

1,基于雪花算法生成

2, 基于数据的自增id生成分布式ID,使用比较简单,缺点是扩展性和可靠性有限;

3、通过uuid生成

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐