欢迎光临散文网 会员登陆 & 注册

Redisson实现防重复提交

2022-05-06 18:23 作者:汇智知了堂  | 我要投稿

防止重复提交的重要性?

在业务开发当中,为什么我们要去想办法解决重复提交这一问题的发生呢?网上的概念有很多:导致表 单重复提交的,造成数据重复的,增加服务器负载的,甚至会造成服务器宕机的,那么为什么会出现这 种情况呢?页面快速的操作以及网络Io问题或者数据库响应慢,这些都会增加后端重复处理的概率, 就拿之前做的项目来说,因为业务性较强,需要进行一个"点赞"的操作,写好业务以后在测试时连续点 击几下,重复地进行点赞和取消点赞操作,因为操作过于频繁,而服务器走过来的响应速度没有那么快 地进行处理,就会导致数据的重复插入情况,最后导致服务器报错,这时,防止重复提交就显得很重要 了。

如何防重复提交?

实现方法有非常多,但是原理大概都是相通的,我选择的是通过使用AOP切面+Redisson来进行处理, 前端发起请求的时候需要在请求头里将token给我,然后我这边通过token+ip再加上请求的路径作为一 个key存到redis里面去,设置一个过期时间,下一次再从缓存当中读取和当前时间进行一个判断,若大 于我们自己设定的超时时间,就进行拦截,不让它进行后面的业务代码,并给出提示“操作频繁,请稍 后重试” 分布式锁方案采用Redisson,这也是官网比较推荐的使用方法。利用分布式锁的特性来防止重复提交。

1.导入依赖

    <dependency

    <groupId>org.redisson</groupId>

     <artifactId>redisson-spring-boot-starter</artifactId

    <version>3.15.0</version

    </dependency>

2.使用注解

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME

@Documented 

public @interface NoRepeat

/**

* 锁名称

*/ 

String lockName() default "no-repeat-default-lock-"

/**

* 参数key,支持Spel 

*/

String key() default "";

/**

* 获取锁的最长时间,单位默认为秒 

*/ 

long waitTime() default 5;

/**

* 租赁时间,单位默认为秒

* 加锁的时间。超过这个时间后锁便自动解开了 

*/

3.编写切面 

    @Slf4j 

    @Aspect 

    @Component 

    public class SystemAspect

    @Resource private RedissonClient redissonClient

    /**

    * SPEL表达式解析器 

    */ 

    private final SpelExpressionParser parser = new SpelExpressionParser(); 

    LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer(); @Around(value = "@annotation(NoRepeat)"

    public Object doNoRepeatAround(ProceedingJoinPoint pjp) throws InterruptedException 

    

    NoRepeat noRepeat = CsUtil.getJoinPointAnnotation(pjp, NoRepeat.class); 

    String key = parseKey(pjp, noRepeat); 

    RLock fairLock = redissonClient.getFairLock(noRepeat.lockName() + key); 

    boolean res = fairLock.tryLock(noRepeat.waitTime(), noRepeat.leaseTime(), TimeUnit.SECONDS); 

    if (res) { 

    log.info("拿到锁");

     try {return pjp.proceed(); 

    } catch (Throwable e) { 

    log.error(e.getMessage(), e); 

    throw new ServiceException(e.getMessage()); 

    } finally { fairLock.unlock(); log.info("释放锁操作"); }

     } else { log.error("重复的请求"); 

    return new Response<Boolean>(999, "重复的请求"); 

        } 

    }

    private String parseKey(ProceedingJoinPoint pjp, NoRepeat noRepeat) { 

    Object[] args = pjp.getArgs(); 

    Method method = ((MethodSignature) pjp.getSignature()).getMethod(); 

    String[] params = discoverer.getParameterNames(method); 

    EvaluationContext context = new StandardEvaluationContext(); 

    for (int len = 0; len < Objects.requireNonNull(params).length; len++) { 

    context.setVariable(params[len], args[len]); }

    String keySpel = noRepeat.key(); Expression keyExpression = parser.parseExpression(keySpel);

     return keyExpression.getValue(context, String.class); 

        } 

    }

4. 使用方式

    @RestController 

    public class TestController

    @ApiOperation("身份绑定(注册)接口"

    @NoRepeat(key = "#user.name"

    @PostMapping("/v1/test/noRepeat"

    public Response<User> doSome(@RequestBody User user) {

     return new Response<>(); 

    }

    @Data 

    static class User

    private String name;

         }

     }

5.展示效果


Redisson实现防重复提交的评论 (共 条)

分享到微博请遵守国家法律