企业级应用组件设计之Redisson分布式锁

今天给大家带来我们的企业级应用组件设计之Redisson分布式锁。

对于redis一定绝大部分Java同学都不陌生,我们的缓存中间件,拥有多路IO单路复用的特性,最显著的特点就是快!我们大多会使用它作用缓冲作用,也就是为我们的MySQL进行读写责任的分担,流量的分担,以及缓存使用,对比计算机本身的 cpu > cache > 磁盘 访问速度的差异,我们也不难推断出 应用本身的JVM堆存 > redis > http等远程端口访问存储 ,我们的数据库MYSQL说白了也是一台部署在3306端口的单体应用,我们可以通过访问IP加此端口进行数据库的权限校验,数据读写操作。那么说回来,redis能作为缓存是因为它的特性快而存储类型合适。其外它还自备了命令setnx命令与expire命令,大部分单体应用可以使用这个命令进行加锁,但是这并不能以分布式的方式被大家都感知到。也就是说当多个主机进行访问同一把锁去做一件事情的时候,我们的redist命令级别很可能导致脏数据与且无法正常的上锁(不过多赘述,仅为大家展示如何设计分布式锁)。

设计Redisson配置类RedissonConfiguration

@Configuration
public class RedissonConfiguration {

    @Value("${spring.data.redis.host}")
    private String redisHost;
    @Value("${spring.data.redis.port}")
    private int port;
    @Value("${spring.data.redis.password}")
    private String password;
    @Bean(destroyMethod = "shutdown",name = "redisson")
    @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
    public RedissonClient redisson() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://"+redisHost+":"+port).setPassword(password);
        config.setCodec(new JsonJacksonCodec());
        return Redisson.create(config);
    }
}

设计基础服务类RedissionLockService

@Component("RedissonLockService")
@ConditionalOnBean(RedissonConfiguration.class)
@Slf4j
public class RedissonLockService {
    @Resource
    private RedissonClient redisson;

    public Boolean lock(String key) {
        return lock(key, 10L);
    }


    public <T> T lock(String key,long expireTime,LockExecutor<T> executor){
        this.lock(key,expireTime);
        T var;
        try{
            var = executor.execute();
        }finally {
            this.unlock(key);
        }
        return var;
    }

    public Boolean redLock(String key) {
        return redLock(key, 10L);
    }

    public <T> T redLock(String key,long expireTime,LockExecutor<T> executor){
        this.redLock(key, expireTime);
        T var;
        try{
            var = executor.execute();
        }finally {
            this.redUnlock(key);
        }
        return var;
    }

    public Boolean lock(String key, Long expireTime) {
        if (Objects.isNull(redisson)) {
            log.warn("redisson客户端对象为null");
            return Boolean.FALSE;
        }
        try {
            RLock lock = redisson.getLock(key);
            if (lock.isLocked()) {
                log.info("锁:{}已被获取,当前请求阻塞",key);
                return Boolean.TRUE;
            }
            lock.lock(expireTime, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            log.warn("获取锁异常");
            return Boolean.TRUE;
        }
    }

    public Boolean redLock(String key, Long expireTime) {
        if (Objects.isNull(redisson)) {
            log.warn("redisson客户端对象为null");
            return Boolean.FALSE;
        }
        try {
            RLock lock = redisson.getLock(key);
            RedissonRedLock redLock = new RedissonRedLock(lock);
            return redLock.tryLock(expireTime, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.warn("获取红锁异常");
            return Boolean.FALSE;
        }
    }

    public Boolean tryLock(String key,Long waitTime,long timeout, TimeUnit unit){
        if (Objects.isNull(redisson)) {
            log.warn("redisson客户端对象为null");
            return Boolean.FALSE;
        }
        try{
            RLock lock = redisson.getLock(key);
            if (lock.isLocked()) {
                log.info("锁:{}已被获取,当前请求阻塞",key);
                return Boolean.TRUE;
            }
            return lock.tryLock(waitTime,timeout,unit);
        }catch(Exception e){
            log.warn("重入锁异常");
            return Boolean.FALSE;
        }
    }

    public Boolean unlock(String key) {
        if (Objects.isNull(redisson)) {
            log.warn("redisson客户端对象为null");
            return Boolean.FALSE;
        }
        try {
            RLock lock = redisson.getLock(key);
            if (lock.isLocked()) {
                log.info("锁:{}释放",key);
               lock.unlock();
            }
            return Boolean.TRUE;
        }catch (Exception e){
            log.warn("释放锁异常");
            return Boolean.FALSE;
        }
    }

    public Boolean redUnlock(String key) {
        if (Objects.isNull(redisson)) {
            log.warn("redisson客户端对象为null");
            return Boolean.FALSE;
        }
        try {
            RLock lock = redisson.getLock(key);
            RedissonRedLock redLock = new RedissonRedLock(lock);
            redLock.unlock();
            return Boolean.TRUE;
        }catch (Exception e){
            log.warn("释放红锁异常");
            return Boolean.FALSE;
        }
    }
}

众所周知,上锁可以上方法级别也可以上代码段级别,就和事务差不多,颗粒度越小。数据的安全性就越好,所以这里先提供方法级别的锁注解,通过AOP切面逻辑解读注解,获取注解内部属性的值去上锁:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Inherited
public @interface RedissonAutoLock {
    long  id();
    long waitTime() default 3L;
    long expireTime() default 10L;
    String description() default "";
    boolean assembleUserInfo() default false;
}

那么解读注解的切面类:

@Aspect
@Component
@DependsOn({"RedissonLockService"})
public class RedissonLockAspect {
    @Resource
    private RedissonLockService redissonLockService;

    @Pointcut("@annotation(com.runjing.learn_runjing.redis.redisson.RedissonAutoLock)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public void lock(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        RedissonAutoLock redissonAutoLock = method.getAnnotation(RedissonAutoLock.class);
        long waitTime = redissonAutoLock.waitTime();
        long expireTime = redissonAutoLock.expireTime();
        long id = redissonAutoLock.id();
        String key = id + method.getName();
        try {
            redissonLockService.lock(key, expireTime);
            try {
                joinPoint.proceed();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            } finally {
                redissonLockService.unlock(key);
            }
        } catch (Exception e) {
            Boolean tryLock = redissonLockService.tryLock(key, waitTime, expireTime, TimeUnit.SECONDS);
            if (tryLock) {
                try {
                    joinPoint.proceed();
                } catch (Throwable ex) {
                    throw new RuntimeException(ex);
                } finally {
                    redissonLockService.unlock(key);
                }
            }
        } finally {
            redissonLockService.unlock(key);
        }
    }

    @AfterReturning("pointcut()")
    public void afterReturning(JoinPoint joinPoint) {

    }

    @AfterThrowing("pointcut()")
    public void afterThrowing(JoinPoint joinPoint) {

    }
}

真正使用时直接在方法上加注解就行:

    @RedissonAutoLock
    public BaseResponse<ErpInventoryCore> getErpInventoryCoreById(Long id) {
        if (Objects.isNull(id)){
            throw new RuntimeException("id为空");
        }
        return BaseResponse.success("", Optional.ofNullable(erpInventoryCoreMapper.getErpInventoryCore(id)).orElse(null));
    }

那么到此为止!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ForestSpringH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值