分布式锁防止订单重复提交_防止表单重复提交看这里!!!

b56295c8b9c04035673441dcee201f68.png

         要解决重复提交这事,先要知道什么是重复提交

假如用户的网速慢,用户点击提交按钮,却因为网速慢,而没有跳转到新的页面,这时的用户会再次点击提交按钮,举个例子:用户点击订单页面,当点击提交按钮的时候,也许因为网速的原因,没有跳转到新的页面,这时的用户会再次点击提交按钮,如果没有经过处理的话,这时用户就会生成两份订单,类似于这种场景都叫重复提交。

好了,为了解决这个问题,可以马上想到通过前端将提交按钮点击后设置倒计时30秒不能再点击就行啊,这些方案是前端的,我们主要说后端方案,看下面的图。防止重复提交后端就是对提交内容加锁即可,判断如果冲突提交就舍弃提交内容,如果没重复就执行后续业务方法。别一提到锁就看不懂,这里的锁你可以理解成唯一值

f1cb082915527a14b1d84ba9b33672e9.png

秉承一贯作风,不讲故事就是干,通过代码理解,先搭建SpringBoot项目

pom坐标

    <parent>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-parentartifactId>        <version>2.1.6.RELEASEversion>    parent>    <dependencies>        <dependency>            <groupId>org.springframework.bootgroupId>            <artifactId>spring-boot-starter-webartifactId>        dependency>        <dependency>            <groupId>org.springframework.bootgroupId>            <artifactId>spring-boot-starter-aopartifactId>        dependency>        <dependency>            <groupId>com.google.guavagroupId>            <artifactId>guavaartifactId>            <version>21.0version>        dependency>    dependencies>

入口类

@SpringBootApplicationpublic class UserApplication {    public static void main(String[] args) {        SpringApplication.run(UserApplication.class, args);    }}

很普通的自定义注解@LocalLock

/** *  单机防止重复提交注解 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic @interface LocalLock {    String key() default "";}

LockMethodInterceptor拦截器类

package cn.wfb.demo.local;import com.google.common.cache.Cache;import com.google.common.cache.CacheBuilder;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.util.StringUtils;import java.lang.reflect.Method;import java.util.concurrent.TimeUnit;/** *  思路是通过aop的方式拦截用户所有请求,前端需要提交唯一的token值 *后端拦截所有请求将token与Cache缓存内容进行比较,如果token存在就抛异常,不存在直接缓存到Cache中 */@Aspect@Configurationpublic class LockMethodInterceptor {    /**     * 通过CacheBuilder设置缓存个数,设置缓存过期时间     */    private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder()            // 最大缓存 1000 个            .maximumSize(1000)            // 设置写缓存后 5 秒钟过期            .expireAfterWrite(5, TimeUnit.SECONDS)            .build();    //所有请求并且带有@LocalLock注解的,进行拦截    @Around("execution(public * *(..)) && @annotation(cn.wfb.demo.local.LocalLock)")    public Object interceptor(ProceedingJoinPoint pjp) {        MethodSignature signature = (MethodSignature) pjp.getSignature();        Method method = signature.getMethod();        LocalLock localLock = method.getAnnotation(LocalLock.class);        String key = getKey(localLock.key(), pjp.getArgs());                //如果该key已经在缓存中,那么抛异常,不存在则存入缓存中        if (!StringUtils.isEmpty(key)) {            if (CACHES.getIfPresent(key) != null) {                throw new RuntimeException("请勿重复请求");            }            // 如果是第一次请求,就将 key 当前对象压入缓存中            CACHES.put(key, key);        }        try {            return pjp.proceed();        } catch (Throwable throwable) {            throw new RuntimeException("服务器异常");        }    }    /**     *     * 本例中传入的key是user:arg[0],被替换参数名user:[token参数内容]     * 两个参数的话user:arg[0],user:arg[1],user:可以省略只为了标识意义明确     *     * @param keyExpress 表达式:例如user:arg[0]     * @param args       参数     * @return 生成的key     */    private String getKey(String keyExpress, Object[] args) {        for (int i = 0; i < args.length; i++) {            //将arg[0]替换成参数的内容            keyExpress = keyExpress.replace("arg[" + i + "]", args[i].toString());        }        return keyExpress;    }}

Controller

@RestController@RequestMapping("/user")public class UserController {    /**     * 通过加本地锁防止重复提交     */    @LocalLock(key = "user:arg[0]")    @GetMapping("/local")    public Map queryLocal(@RequestParam String token) {        HashMap map = new HashMap();        map.put("success", token);        return map;    }}

测试localhost:8080/user/local?token=‘唯一ID值’  其实整体思路很简单,这个token参数的值应该让前端通过工具类产生唯一值之后提交。提交到后端通过AOP拦截请求,将token内容先判断是否已存在Cache(google的工具类)的缓存,不存在则缓存到Cache中,如果已存在直接抛异常。整体代码很好理解。

那么为什么说分布式系统要和传统的方式区别开,这里的原因大家应该能明白就是缓存位置问题,如果传统项目用本地缓存保存即可,但是分布式就需要Redis作为共享内存,道理是一样的代码都类似,只需要将本例中google的本地Cache缓存改成redis保存即可

采用原生 API 来实现Redis的分布式锁代码如下

//connection.set(前端token的字节,字节数组,通过Expiration设置key时间,使用的Redis的指令是SET_IF_ABSENT)final Boolean success = redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.set(lockKey.getBytes(), new byte[0], Expiration.from(lock.expire(), lock.timeUnit()), RedisStringCommands.SetOption.SET_IF_ABSENT));

其他想问的也可以进群聊

d4600272f5396ce2ee3bbf6643f141e2.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/416514.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python实战之编码问题:中文!永远的痛

python实战之编码问题&#xff1a;中文&#xff01;永远的痛 编码的思维图谱&#xff1a; 也就是说文件没有编码之说&#xff0c;事实上都是按二进制格式保存在硬盘中的。不过在写入读取时须使用相应的编码进行处理&#xff0c;以便操作系统配合相关软件/字体&#xff0c;绘制到…

自动打包_全自动打包机行业发展如何?全自动打包机行业发展现状分析

全自动打包机行业发展如何&#xff1f;全自动打包机行业发展现状分析随着经济的快速发展&#xff0c;现在很多产品的生产过程都变得简单起来&#xff0c;更新换代也非常快&#xff0c;这不仅促进了企业的发展&#xff0c;也提高了人们生活质量。全自动打包机作为企业生产中常见…

小可爱

转载于:https://www.cnblogs.com/lrf9606/p/7077434.html

13新功能_再聊聊灵感盒 -Marginnote 3.6.12/13新功能

我是夜雨&#xff0c;水群最多的一类人本文主要BB了我对灵感盒的理解Marginnote 3.6.12/13个人之前对灵感盒的理解在此强调开发者的一句话不要对灵感盒做太多高大上的引申灵感盒只不过是新的脑图结构关于灵感盒的延伸Zattelkasten/slip box/卡片盒笔记法该内容很早之前就在Marg…

局域网抢答器_基于童芯派的抢答器V1.0

[童心制物Makeblock]的新产品"童芯派"发布已两月有余&#xff0c;刚一发布就第一时间入手三个含扩展板的套装&#xff0c;Makeblock的产品我还是很认可的&#xff0c;从mbot&#xff0c;ranger到程小奔&#xff0c;从神经元&#xff0c;光环版到童芯派&#xff0c;东…

下拉框_教你封装 Element Tree 树状下拉框

在日常项目开发中&#xff0c;树状下拉框的需求还是比较常见的&#xff0c;但是element并没有这种组件以供使用。在这里&#xff0c;小编就基于element如何封装一个树状下拉框做个详细的介绍。通过这篇文章&#xff0c;你可以了解学习到一个树状下拉框组件是如何一步一步封装成…

字体单独设置样式_Glyphs 官方教程 | 字体命名

​​字体名称是很重要的&#xff0c;它决定了字体菜单中的分组和顺序&#xff0c;而这直接影响你的字体将如何呈现给用户。在一款字体中&#xff0c;字体名称被存储在六个不同的地方&#xff0c;这一点已经相当困难&#xff1b;或者实际上还会有更多的地方&#xff0c;这就更复…

委外订单_ERP软件教程:金蝶ERP的委外加工业务流程(一)

ERP软件教程&#xff1a;金蝶ERP的委外加工业务流程关注我&#xff0c;我将定期分享更多的ERP解决方案转发关注并私信我&#xff0c;了解更多的解决方案及操作方法哦欢迎大家随时咨询关于金蝶ERP的任何问题&#xff01;一、应用软件版本&#xff1a;金蝶KIS旗舰版6.0二、应用场…