分布式设计
11.说说什么是幂等性?
什么是幂等性?
幂等性是一个数学概念,用在接口上:用在接口上就可以理解为:同一个接口,多次发出同一个请求,请求的结果是一致的。
简单说,就是多次调用如一次。
什么是幂等性问题?
在系统的运行中,可能会出现这样的问题:
用户在填写某些form表单时,保存按钮不小心快速点了两次,表中竟然产生了两条重复的数据,只是id不一样。
开发人员在项目中为了解决接口超时问题,通常会引入了重试机制。第一次请求接口超时了,请求方没能及时获取返回结果(此时有可能已经成功了),于是会对该请求重试几次,这样也会产生重复的数据。
mq消费者在读取消息时,有时候会读取到重复消息,也会产生重复的数据。
这些都是常见的幂等性问题。
在分布式系统里,只要下游服务有写(保存、更新)的操作,都有可能会产生幂等性问题。
PS:幂等和防重有些不同,防重强调的防止数据重复,幂等强调的是多次调用如一次,防重包含幂等。
怎么保证接口幂等性?
接口幂等性
insert前先select在保存数据的接口中,在insert前,先根据requestId等字段先select一下数据。如果该数据已存在,则直接返回,如果不存在,才执行 insert操作。
加唯一索引加唯一索引是个非常简单但很有效的办法,如果重复插入数据的话,就会抛出异常,为了保证幂等性,一般需要捕获这个异常。如果是java程序需要捕获:DuplicateKeyException异常,如果使用了spring框架还需要捕获:MySQLIntegrityConstraintViolationException异常。
加悲观锁更新逻辑,比如更新用户账户余额,可以加悲观锁,把对应用户的哪一行数据锁住。同一时刻只允许一个请求获得锁,其他请求则等待。
加乐观锁更新逻辑,也可以用乐观锁,性能更好。可以在表中增加一个timestamp或者version字段,例如version:在更新前,先查询一下数据,将version也作为更新的条件,同时也更新version:
建防重表有时候表中并非所有的场景都不允许产生重复的数据,只有某些特定场景才不允许。这时候,就可以使用防重表的方式。例如消息消费中,创建防重表,存储消息的唯一ID,消费时先去查询是否已经消费,已经消费直接返回成功。
状态机有些业务表是有状态的,比如订单表中有:1-下单、2-已支付、3-完成、4-撤销等状态,可以通过限制状态的流动来完成幂等。
分布式锁直接在数据库上加锁的做法性能不够友好,可以使用分布式锁的方式,目前最流行的分布式锁实现是通过Redis,具体实现一般都是使用Redission框架。
token机制请求接口之前,需要先获取一个唯一的token,再带着这个token去完成业务操作,服务端根据这个token是否存在,来判断是否是重复的请求。
分布式限流
12.你了解哪些限流算法?
计数器
计数器比较简单粗暴,比如我们要限制1s能够通过的请求数,实现的思路就是从第一个请求进来开始计时,在接下来的1s内,每个请求进来请求数就+1,超过最大请求数的请求会被拒绝,等到1s结束后计数清零,重新开始计数。
这种方式有个很大的弊端:比如前10ms已经通过了最大的请求数,那么后面的990ms的请求只能拒绝,这种现象叫做“突刺现象”。
漏桶算法
就是桶底出水的速度恒定,进水的速度可能快慢不一,但是当进水量大于出水量的时候,水会被装在桶里,不会直接被丢弃;但是桶也是有容量限制的,当桶装满水后溢出的部分还是会被丢弃的。
算法实现:可以准备一个队列来保存暂时处理不了的请求,然后通过一个线程池定期从队列中获取请求来执行。
漏桶算法
令牌桶算法
令牌桶就是生产访问令牌的一个地方,生产的速度恒定,用户访问的时候当桶中有令牌时就可以访问,否则将触发限流。
实现方案:Guava RateLimiter限流
Guava RateLimiter是一个谷歌提供的限流,其基于令牌桶算法,比较适用于单实例的系统。
令牌桶算法
领取专属 10元无门槛券
私享最新 技术干货