vlambda博客
学习文章列表

「解决方案」SpringBoot项目中如何解决并发导致的重复提交问题

优质文章,第一时间送达

来源:http://suo.im/66liCE

本文前篇是对场景的分析,后篇会有解决方案,读完本篇你将可以仅仅使用两个注解即可解决并发重复提交问题。

可以直接看方案四,直接读推荐解决方案。

场景分析

重复提交问题是一个老生常谈的问题,项目中经常会遇到这种情况,这种情况在查询类接口其实也没有太大问题,但是如果是在设计修改数据的接口就有会严重问题,但是这种情况并也不难处理,因为我们的代码最少会做一个幂等判断,即会先有一个查询动作,查询不到才会放行。但是难就难在假如说是并发加重复提交这种场景就很难处理。这个时候就不得不去思考新的解决方案。

解决方案

方案一、

通过数据库唯一索引来解决,即在数据库创建一张唯一表,在每次数据请求时候将唯一键作为数据插入这张唯一表中,正常情况是可以插入成功的,当出现重复提交情况就会异常提示。

缺点

  1. 数据库性能问题,因为每次操作都设计到数据库的一次插入动作,所以可能会有性能问题

  2. 数据只有一次处理机会,当第一次处理失败,第二次在进来就当重复给拦截了

方案二、

token令牌,后端提供一个生成令牌的接口,前端在每次进行数据访问时候,先拿去token令牌,后端通过对token令牌的生命周期管控,来解决重复提交问题

缺点: 前后端改造大,后端要单独维护一个接口,前端每次请求也要多调一个接口

总结

希望通过查询+修改方式来解决并发和重复提交问题都是不现实的,因为不能保证查询和修改是一个原子性操作,所以只要并发就很容易突破这种方式的防重逻辑。那么如何解决这个问题呢? 其实就是保证防重逻辑的原子性操作。同样也是两种方案。

方案三、

类似于通过数据库唯一索引这种方式,不同的是将数据库换成内存缓存即项目里定义一个Cache集合缓存可以用Guava的缓存框架,设置缓存时间和缓存数量来解决。不过也是有缺点的,缺点就是不满足分布式要求,当请求打到其他应用服务器就突破了这种情况。所以不建议使用这种方案。如果是单机器可以考虑。

方案四、

是对上一种方案的改进,通过Redis实现,每次请求都插入Redis数据库中,并设置过期时间, 既能满足性能需求,同时也满足分布式情况。同时Redis因为是单线程的所以也能保证原子性。综上所述这种方案应该是最好的。

  • 满足原子性

  • 满足分布式环境应用

  • 性能有保证

  • 支持重试(通过设置过期时间)


伪代码如下


「解决方案」SpringBoot项目中如何解决并发导致的重复提交问题

终结解决方案

该方案是对上面方案四的一个实现,感兴趣的同学一个start一下,然后拉下来看看实现原理。

核心原理就是方案四中提的,通过拦截和自动配置无缝整合到SpringBoot项目中使用。

「解决方案」SpringBoot项目中如何解决并发导致的重复提交问题

使用方式

「解决方案」SpringBoot项目中如何解决并发导致的重复提交问题

如何判断是否引用成功

当出现tomato Logo即说明启用成功

「解决方案」SpringBoot项目中如何解决并发导致的重复提交问题

感兴趣的同学可以学习一下代码,提出任何问题小编都会第一时间回复。一起探讨学习。

接下来小编会围绕Redis做更多的实战分享目前定下来的两个议题是:

1.基于Redis原子性操作实战应用一之并发拦截 「Tomato」

2.基于Redis原子性操作实战应用二之防洪限流「Easy-Sentinel」

这两个议题其实实战代码都已经写好了,只是还没有总结成文,感兴趣的同学可以先到github上拉去实战代码。Tomato就是解决并发导致的重复提交,而Easy-Sentinel会更高级一点,利用Redis+Lua脚本实现原子性操作,从而来达到防洪限流的能力。


关注程序员闪充宝后台回复“666”和“111免费领取46阶段以及实战java视频资料



看完本文有收获?请转发分享给更多人


你在看?