SpringCloud Redisson分布式锁

Java 提供的原生锁机制在多机部署场景下失效了。这是因为两台机器加的锁不是同一个锁 (两个锁在不同的 JVM 里面)。那么,只要保证两台机器加的锁是同一个锁,问题不就解决了吗?此时,就该分布式锁隆重登场了,分布式锁的思路是:在整个系统提供一个全局、唯一的获取锁的 “东西”,然后每个系统在需要加锁时,都去问这个“东西” 拿到一把锁,这样不同的系统拿到的就可以认为是同一把锁。

一般提及到Redis的分布式锁我们更多的使用的是Redisson的分布式锁。

1、分布式锁思路分析
锁特点:
排他性:同一时间,只有一个线程能获得;
阻塞性:其它未抢到的线程阻塞等待,直到锁被释放,再继续抢;
可重入性:线程获得锁后,后续是否可重复获取该锁(避免死锁)。
高性能和高可用: 加锁和解锁需要高效,同时也需要保证高可用,防止分布式锁失效
具备阻塞和非阻塞性:能够及时从阻塞状态中被唤醒
当然,还要考虑性能开销等问题。

Redission使用
  • 1.redisson实现限流
  • 2.redisson实现延迟队列
  • 3.redisson实现分布式锁

防止重复提交问题 redis锁

1、何为重复提交
重复提交是在第一次请求已经在进行处理或处理成功的情况下,人为的进行多次操作,导致不满足幂等要求的服务多次改变状态。
2、何为幂等
幂等是其任意多次执行所产生的影响均与一次执行的影响相同(不用担心重复执行会对系统造成改变)。

3、何时使用
场景一:在网络延迟的情况下让用户有时间点击多次submit按钮导致表单重复提交
场景二:表单提交后用户点击【刷新】按钮导致表单重复提交
场景三:用户提交表单后,点击浏览器的【后退】按钮回退到表单页面后进行再次提交。

tryLock

@Aspect
@Component
public class RepeatSubmitAOP {
    @Resource private RedissonClient redisson;

    @Pointcut("@annotation(com.java.pojo.NoRepeatSubmit)")
    public void pointCutNoRepeatSubmit() {
    }

    /** 校验是否为重复提交 */
    @Around("pointCutNoRepeatSubmit()")
    public Object executeVerify(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        NoRepeatSubmit noRepeatSubmit = method.getAnnotation(NoRepeatSubmit.class);
        //
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = requestAttributes.getResponse();
        HttpServletRequest request = requestAttributes.getRequest();
        String token = request.getHeader("token");
        String path = request.getServletPath();
        String key = "REPEAT_SUBMIT_" + path + "_" + token;
        RLock lock = redisson.getLock(key);
        try {
            boolean isLocked = lock.tryLock(0, noRepeatSubmit.lockTime(), TimeUnit.SECONDS);
            if(isLocked){
                return joinPoint.proceed();
            }else{
                Assert.isTrue(false, "请勿重复提交表单");
            }
        } catch (Exception e) {
            log.error("exception because locked: " + e.getMessage());
        }
        response.getWriter().print(JSONObject.toJSONString(Res.fail(ResEntity.Err.DUPLICATE_OPERATE)));
        return null;


//        StringBuilder sb = new StringBuilder();
//        sb.append(joinPoint.getTarget().getClass().getName()).append(method.getName()); //类名+方法名
//        String key = "REPEAT_SUBMIT_" + sb.toString();
//        RLock lock = redisson.getLock(key);
//        try {
//            boolean isLocked = lock.tryLock(0, noRepeatSubmit.lockTime(), TimeUnit.SECONDS);
//            if(isLocked){
//                return joinPoint.proceed();
//            }else{
//                Assert.isTrue(false, "请勿重复提交表单");
//            }
//        } catch (Exception e) {
//            log.error("exception because locked: " + e.getMessage());
//        }
//        response.getWriter().print(JSONObject.toJSONString(Res.fail(ResEntity.Err.DUPLICATE_OPERATE)));
//        return null;
    }

}

/**
 * 防重复提交注解
 * date: 2023/3/3
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoRepeatSubmit {
    int lockTime() default 5; //上锁时间多久后可以再次提交, 默认5s
}

二进制流存储

    /**
     * 二进制流
     * 提供了InputStream接口和OutputStream接口的实现
     */
    @Test
    public void RedissonStream() throws IOException {
        Config config = new Config();
        //config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redissonClient = Redisson.create(config);//创建客户端
        //====================操作流来存储对象====================
        RBinaryStream stream = redissonClient.getBinaryStream("stream");
        stream.set("name is ".getBytes());
        OutputStream outputStream = stream.getOutputStream();
        outputStream.write("victory".getBytes());
        InputStream inputStream = stream.getInputStream();
 
        ByteArrayOutputStream result = new ByteArrayOutputStream();
        byte[] bytes = new byte[1024];
        int length;
        while ((length = inputStream.read(bytes)) != -1) {
            result.write(bytes, 0, length);
        }
        System.out.println(result.toString());
        redissonClient.shutdown(); //关闭客户端
    }

问题

引入Redisson可能会出现项目启动失败问题解决
https://blog.csdn.net/bengbuguang4321/article/details/121951650

Redis 连接失败 参数说明 redis Can’t init enough connections amount!
https://www.cnblogs.com/songanwei/p/9171520.html

布隆过滤器
https://www.cnblogs.com/cj8357475/p/16539864.html

你可能感兴趣的:(SpringCloud,分布式,spring,cloud)