如何避免并发情况下的重复提交

夏日阳光
176
文章
22
评论
2019年10月11日21:30:44 评论 223 1041字阅读3分28秒

在业务开发中,我们常会面对防止重复请求的问题。当服务端对于请求的响应涉及数据的修改,或状态的变更时,可能会造成极大的危害。重复请求的后果在交易系统、售后维权,以及支付系统中尤其严重。

如何避免并发情况下的重复提交

在传统的restful风格的项目中,防止重复提交,通常做法是:后端生成一个唯一的提交令牌(uuid),并存储在服务端。页面提交请求携带这个提交令牌,后端验证并在第一次验证后删除该令牌,保证提交请求的唯一性。

上述的思路其实没有问题的,但是需要前后端都稍加改动,如果在业务开发完在加这个的话,改动量未免有些大了,本节的实现方案无需前端配合,纯后端处理。

利用分布式锁

相同的请求在同一时间只能被处理一次,利用分布式锁可以非常方便地解决这个问题。

思路:

1、自定义注解 @NoRepeatSubmit 标记所有Controller中的提交请求。
2、通过拦截器对所有标记了 @NoRepeatSubmit 的方法拦截。
3、在业务方法执行前,获取当前用户的 token(或者JSessionId)+ 当前请求地址,作为一个唯一 KEY,去获取 Redis 分布式锁(如果此时并发获取,只有一个线程会成功获取锁)。
4、业务方法执行后,释放锁。

拦截器类

利用数据库的唯一索引

如果设置了唯一约束,那么同一条数据再次插入数据库时,数据库会报唯一索引的错误,这个时候后台对这个异常进行处理并返回给前端提示。

思路:

接口A接收到请求之后,对请求信息hash运算,得到hash值hashCodeA;
保存hashCodeA 到数据库,并且对应的数据库的列(column)满足unique约束;
保存成功之后,才进行正常业务逻辑处理,比如提交订单;
服务器B接收到相同的请求后,也得到相同的hash值,hashCodeA,
服务器B保存hashCodeA 到数据库,肯定失败,因为相同的hash值已经存在;
因为保存失败,所以后面的业务逻辑不会执行。

缓存计数器
由于数据库的操作比较消耗性能,了解到redis的计数器也是原子性操作。果断采用计数器。既可以提高性能,还不用存储,而且能提升qps的峰值。

以支付为例子:

每次request进来则新建一个以orderId为key的计数器,然后+1。

如果>1(不能获得锁): 说明有操作在进行,删除。
如果=1(获得锁): 可以操作。
操作结束(删除锁):删除这个计数器。

weinxin
关于本站
本站是一个分享建站经验、网站优化以及互联网技巧的个人博客。
  • 版权声明: 发表于 2019年10月11日21:30:44
  • 转载注明:https://pieruo.com/13297.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: