微服务热点之幂等性问题解决方案

引言

  受到疫情的影响,很多企业都采用了在家办公的方式,希望疫情可以早点结束,这样我们就可以拥抱美好的春天了。言归正传,今天我们来对微服务中的热门问题: 幂等性 来进行探究。

何为幂等性

  在开发的过程中,我们常常听到 “保证接口幂等” 这样的话,从字面角度理解即接口在多次调用的情况下,最终返回的结果是一致的。但我认为这种理解并不准确。举个最简单的例子:A查询数据,B插入数据,A第一次查询数据得到结果,B随后插入数据,A再次查询,此时得到的结果必然跟第一次不同,如果按照上面的说法,接口就不是幂等的,这明显是不正确的。

  正确的理解应该是 接口多次调用对系统产生的影响不变,但允许返回的值不同。既然如此,怎么算违背了幂等性呢?

  比如A新增数据,点击新增,系统迟迟没有响应,此时A对页面进行刷新,系统提示新增成功,但是数据库里却多了两条数据,这就是典型的重复提交问题,同时它违背了接口的幂等性原则。

  又比如A增加20条库存,点击增加之后系统又迟迟没有响应,A对页面刷新,提示增加库存成功,此时A去查看库存,发现库存多了20个,这也违背了幂等性原则。

  以上两个例子为新增和修改操作,同时新增操作应该是缺少了唯一约束,导致数据重复提交,修改操作为计算赋值,而非直接赋值,导致了问题的发生。

  至于查询和删除操作,这二者是具备幂等性的。查询: select * from xxx,此操作不会使数据发生变化,具备幂等性。删除:delete from xxx where xxx,多次操作,结果相同,具备幂等性。

如何解决幂等问题

  幂等问题的解决方案有多种,在这里主要介绍以下四种方案: token校验乐观锁唯一主键去重表

token校验

  顾名思义,此种方式在每次请求的时候会携带一个token(令牌),后台根据token来判断本次请求是否可以执行。常用的方式为 token+redis ,此方案几乎适合所有的幂等场景。流程图如下所示:
流程图
此方案的设计思想:

  1.服务端提供获取token的接口暴露给客户端。对于存在幂等问题的业务执行之前,都要先去请求接口获取对应的token,然后存入redis之中。

  2.客户端成功获取token之后,才真正的去调用业务接口(携带着token)。

  3.服务端判断token是否存在于redis之中,如果存在则代表此请求为首次请求,继续执行业务,业务执行成功之后。删除redis中的token。如果不存在则证明本次为重复请求,给客户端返回友好提示即可,有效的保证了业务代码不被重复执行。

此方案的一个坑——先删除token还是后删除token:

  在以上对本方案的讲解中,我们采用的是后删除token的方式: 业务代码执行完毕,删除token ,此方式存在一个问题:业务执行成功之后,token删除失败,这样如果用户疯狂刷新,就可能导致大量重复提交的问题发生。

  先删除token同样也存在问题,删除token后如果业务处理发生异常,客户端没有收到明确的结果,用户进行刷新重试,由于token已经被删除,所以会认为是重复请求,一直无法请求成功(除非回到原页面重新请求,刷新已无效)。

  在这里推荐先删除token,业务出现异常之后,客户端可以再重新请求一次获取新的token,再重试就OK了,总好过token删除失败之后引发重复提交的问题。

此方案的缺点:

  缺点显而易见:每次业务请求之前都附带了一个额外获取token的请求,但redis的读写速度很快,如果不是为了追求极致的性能,这点影响可以忽略不计。

乐观锁

  对于乐观锁,相信大家应该比较熟悉,这也算老生常谈了,乐观锁主要解决的是 计算赋值型的修改场景,如下sql:

update table 
set A=A+20,version=version+1
where id=1 and version=1

  为业务相关的表加上版本号字段之后,计算赋值型业务就具备了幂等性(直接赋值操作是幂等的)。同时乐观锁在高并发之中也有着广泛的运用,这里我们暂且不谈。

  乐观锁的缺点也十分明显:执行业务之前,需要查询出当前的版本号。

唯一主键

  这个方法是利用了数据库主键唯一的特性,可以解决 新增场景的幂等性问题 ,当然这里的主键需要我们自己生成,但如果是分库分表的业务场景,相同请求需要落地到同一个数据库中的同一个表中,不然唯一主键就失去了意义。

去重表

  去重表类似唯一主键,只不过去重表与业务无关,实现了业务上的解耦,在执行insert的时候,把唯一主键插入到去重表之中,如果插入失败,则证明为重复提交操作,有效避免了幂等问题,多个业务表可以共用一个去重表。

总结

  以上便是几种常用的幂等性解决方案,我们可以根据自身业务灵活选择,在进行系统设计的时候,需要我们秉承一个原则:避免系统过度复杂,一般来说,普通的项目 乐观锁和唯一主键 已然足矣,并且实现简单。今天就到这了,学如逆水行舟,不进则退,在此与君共勉!

发布了34 篇原创文章 · 获赞 113 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/m0_37719874/article/details/104358124
今日推荐