背景
资料的维护接口和提供给第三方的接口都在一个项目里,是分布式服务。大部分数据都有唯一索引约束,但是有个业务表没加索引。测试ok就上线了。
期望效果:点击【提交】按钮发送请求后,按钮会失去焦点,页面弹出遮罩层,后端服务响应,前端收到回调信息给出提示,页面刷新。
实际效果:用户电脑硬件和网络条件不好,经常转圈圈,按钮可以连续点击,请求被分发到了多个服务或者同一个服务受到多次请求。因为没做幂等性处理,出现脏数据。
思路
1、首先考虑的就是唯一索引:
这个实现起来简单快速,能从最底层断绝重复问题。但是要求业务数据要有唯一属性;分库分表使用不了。
2、然后考虑怎么拒绝用户的重复请求:
请求过来了,给用户按照业务类型发放令牌,没有获得令牌就直接返回。
3、令牌的生成方式:
请求来时,没有记录则发放令牌,有,时间小于2秒不发放,过了2秒则发放,同时记录发放时间。当然,要有个记录的过期时间。
4、分布式锁:
生成令牌,需要一个全局(分布式)的悲观锁,网上有很多分布式锁的实现文章,因为项目已经使用了redis集群,所以最终选用redis实现,而且可以直接利用缓存失效时间。具体代码就不重复粘贴了。
使用心得:
1)可以使用sessionId和ServletPath作为key,也可以使用用户的id之类的字段。
2)缓存失效时间可以在调用时传入,链路长,处理慢的可以长一些。
3)可以做工具类,在方法内调用,好处是可以主动调用缓存失效方法,在获取令牌失败后,灵活添加其他业务处理代码;
也可以做成注释,简洁美观。利用spring的切面编程和 @Around注释,可以统一处理,并且把结果返回给调用方。
如果需要代码可以参考文章:https://blog.csdn.net/zhuyanlin09/article/details/91626111