阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

简单介绍redis分布式锁解决表单重复提交的问题

73次阅读
没有评论

共计 2651 个字符,预计需要花费 7 分钟才能阅读完成。

导读 在系统中,有些接口如果重复提交,可能会造成脏数据或者其他的严重的问题,所以我们一般会对与数据库有交互的接口进行重复处理。本文就详细的介绍一下 redis 分布式锁解决表单重复提交,感兴趣的可以了解一下

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

使用 redis 的 setnx 和 getset 命令解决表单重复提交的问题。

1. 引入 redis 依赖和 aop 依赖

org.springframework.boot
    spring-boot-starter-redis
    1.3.8.RELEASE

    org.springframework.boot
    spring-boot-starter-aop

2. 编写加锁和解锁的方法。

/**
 * @author wangbin
 * @description redis 分布式锁
 * @date 2019 年 09 月 20 日
 */
@Component
public class RedisLock {private final Logger logger = LoggerFactory.getLogger(RedisLock.class);
 
    @Autowired
    private StringRedisTemplate redisTemplate;
 
    /**
     * @author wangbin
     * @description 进行加锁的操作 (该方法是单线程运行的)
     * @date 2019 年 09 月 20 日
     * @param key 某个方法请求 url 加上 cookie 中的用户身份使用 md5 加密生成
     * @param value 当前时间 + 过期时间 (10 秒)
     * @return true 表示加锁成功   false 表示未获取到锁
     */
    public boolean lock(String key,String value){
        // 加锁成功返回 true
        if(redisTemplate.opsForValue().setIfAbsent(key,value,10, TimeUnit.SECONDS)){return true;}
        String currentValue = redisTemplate.opsForValue().get(key);
        // 加锁失败,再判断是否由于解锁失败造成了死锁的情况
        if(StringUtils.isNotEmpty(currentValue) && Long.parseLong(currentValue) 

3. 使用拦截器在请求之前进行加锁的判断。

@Configuration
public class LoginInterceptor extends HandlerInterceptorAdapter {private final Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
    // 超时时间设置为 10 秒
    private static final int timeOut = 10000;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisLock redisLock;
    /**
     * 在请求处理之前进行调用(Controller 方法调用之前)* 基于 URL 实现的拦截器
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String path = request.getServletPath();
        if (path.matches(Constants.NO_INTERCEPTOR_PATH)) {
            // 不需要的拦截直接过
            return true;
        } else {
            // 这写你拦截需要干的事儿,比如取缓存,SESSION,权限判断等
            // 判断是否是重复提交的请求
            if(!redisLock.lock(DigestUtils.md5Hex(request.getRequestURI()+value),String.valueOf(System.currentTimeMillis()+timeOut))){logger.info("=========== 获取锁失败,该请求为重复提交请求");
                        return false;
             }
            return true;
        }
    }
}

4. 使用 aop 在后置通知中进行解锁。

/**
 * @author wangbin
 * @description 使用 redis 分布式锁解决表单重复提交的问题
 * @date 2019 年 09 月 20 日
 */
@Aspect
@Component
public class RepeatedSubmit {
 
    @Autowired
    private RedisLock redisLock;
 
    // 定义切点
    @Pointcut("execution(public * com.kunluntop.logistics.controller..*.*(..))")
    public void pointcut(){}
 
    // 在方法执行完成后释放锁
    @After("pointcut()")
    public void after(){ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        redisLock.unLock(DigestUtils.md5Hex(request.getRequestURI()+ CookieUtils.getCookie(request,"userkey")));
    }
}

到此这篇关于 redis 分布式锁解决表单重复提交的问题的文章就介绍到这了。

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-25发表,共计2651字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中