springboot之Redisson使用及使用Aop优化注解实现


  • 说明

  • 配置方式

  • 使用

  • 优化

    • 实现类总览

    • 分布式锁抽象接口

    • 添加分布式锁注解

    • 抽象分布式锁接口

  • 定义切面

  • 测试

    • controller


说明

redisson不用多说,都知道是解决redis分布式锁的一个jar。

配置方式

redisson配置方法有两种:

  1. 基于JavaBean配置
Config config = new Config();
config.setTransportMode(TransportMode.EPOLL);
config.useClusterServers()
       // use "rediss://" for SSL connection
      .addNodeAddress("perredis://127.0.0.1:7181");

RedissonClient redisson = Redisson.create(config);
  1. 基于YAML配置

Yaml 也有如下两种方式

Config config = new Config();
// ... many settings are set here
String yamlFormat = config.toYAML();

// 或者使用如下格式
Config config = Config.fromYAML(new File("config-file.yaml"));  
RedissonClient redisson = Redisson.create(config);

yaml配置文件配置如下:

singleServerConfig:
  address: "redis://127.0.0.1:${REDIS_PORT:-6379}"

这是基础的配置,一般使用这些基础配置就可以简单的使用了,当然还有一些其他配置项目如下:

springboot之Redisson使用及使用Aop优化注解实现看源码看到还是有很多其他配置项的,看源码和注释大概都能知道这些配置都是干嘛的,这里就不作过多的解释,如果看源码不懂还可以去看官方文档,这里给出官方文档地址:https://github.com/redisson/redisson/wiki/2.-Configuration

这里我使用的是单机的配置,因为现在大多的是用的云redis。所以在我们使用就是单机的,但是云产商是给我们做了高可用的,所以我们不需要使用集群配置,具体集群配置可以看官网wiki

使用

我这里使用的springboot,配置方式和上面没什么差别

@Configuration
public class RedissonConfig {

    @Value("${redis.host}")
    private String redisLoginHost;
    @Value("${redis.port}")
    private Integer redisLoginPort;
    @Value("${redis.password}")
    private String redisLoginPassword;


    @Bean
    public RedissonClient redissonClient() {
        return createRedis(redisLoginHost, redisLoginPort, redisLoginPassword);
    }

    private RedissonClient createRedis(String redisHost, Integer redisPort, String redisPassword) {
        Config config = new Config();
        SingleServerConfig singleServerConfig = config.useSingleServer();
        singleServerConfig.setAddress("redis://" + redisHost + ":" + redisPort + "");
        if (DataUtils.isNotEmpty(redisPassword)) {
            singleServerConfig.setPassword(redisPassword);
        }
        config.setCodec(FastjsonCodec.INSTANCE);
        return Redisson.create(config);
    }
}

配置完了使用就很简单了,一般是这种方式


@Autowired
RedissonClient redissonClient;

public void test(){
RLock lock = redissonClient.getLock("order:1");
lock.lock(10, TimeUnit.SECONDS);
try {
finally {
            lock.unlock();
        }
}

这是最简单的使用,可以看到每次使用还是比较麻烦的。我们就基于Spring AOP做一个优化,加一个注解就能实现分布式锁。

优化

实现类总览

springboot之Redisson使用及使用Aop优化注解实现
在这里插入图片描述

分布式锁抽象接口

  • DistributedLock.java
public interface DistributedLock<T{

    /**
     * 分布式锁逻辑 代码块
     */

    process();

    /**
     * 分布式锁名
     */

    String lockName();
}

这是后面再aop使用的,抽象出了 分布式锁代码块和分布式锁名,面向接口编程

添加分布式锁注解

  • CiderDistributedLock.java
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CiderDistributedLock {

    /**
     * 锁名字
     */

    String lockName() default "";

    /**
     * 锁前缀
     */

    String lockNamePre() default "";

    /**
     * 锁后缀
     */

    String lockNamePost() default "";

    /**
     * 锁前后缀拼接分隔符
     */

    String separator() default "_";

    /**
     * 是否使用公平锁
     */

    boolean fairLock() default false;

    /**
     * 是否使用尝试锁
     */

    boolean tryLock() default false;

    /**
     * 尝试锁最长等待时间
     */

    long tryWaitTime() default 30L;

    /**
     * 锁超时时间,超时自动释放锁
     */

    long outTime() default 20L;

    /**
     * 时间单位 默认秒
     */

    TimeUnit timeUnit() default TimeUnit.SECONDS;

}

抽象分布式锁接口

  • DistributedLockTemplate.java
public interface DistributedLockTemplate {

    /**
     * 默认超时锁释放时间
     */

    long DEFAULT_OUT_TIME = 5;
    /**
     * 默认尝试加锁时间
     */

    long DEFAULT_TRY_OUT_TIME   = 30;
    /**
     * 默认时间单位
     */

    TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;

    /**
     * 加锁
     * @param distributedLock
     * @param fairLock 是否使用公平锁
     * @param <T>
     * @return
     */

    <T> lock(DistributedLock<T> distributedLock, boolean fairLock);

    /**
     *
     * @param distributedLock
     * @param outTime 锁超时时间。超时后自动释放锁
     * @param timeUnit 时间单位
     * @param fairLock 是否使用公平锁
     * @param <T>
     * @return
     */

    <T> lock(DistributedLock<T> distributedLock, long outTime, TimeUnit timeUnit, boolean fairLock);

    /**
     * 尝试加锁
     * @param distributedLock
     * @param fairLock 是否使用公平锁
     * @param <T>
     * @return
     */

    <T> tryLock(DistributedLock<T> distributedLock, boolean fairLock);

    /**
     *
     * @param distributedLock
     * @param tryOutTime 尝试获取锁时间
     * @param outTime 锁超时时间
     * @param timeUnit 时间单位
     * @param fairLock 是否使用公平锁
     * @param <T>
     * @return
     */

    <T> tryLock(DistributedLock<T> distributedLock, long tryOutTime, long outTime, TimeUnit timeUnit, boolean fairLock);


}

这里有了分布式锁接口,后续可能有其他实现方式,都是基于这个接口来的,我们这次就直说先实现一个单机版本的分布式锁实现类

  • SingleDistributedLockTemplate.java
@Service
public class SingleDistributedLockTemplate implements DistributedLockTemplate{

    @Autowired
    private RedissonClient redisson;

    @Override
    public <T> lock(DistributedLock<T> distributedLock, boolean fairLock) {
        return lock(distributedLock, DEFAULT_OUT_TIME, DEFAULT_TIME_UNIT, fairLock);
    }

    @Override
    public <T> lock(DistributedLock<T> distributedLock, long outTime, TimeUnit timeUnit, boolean fairLock) {
        RLock lock = getLock(distributedLock.lockName(), fairLock);
        try {
            lock.lock(outTime, timeUnit);
            return distributedLock.process();
        } finally {
            if (lock != null && lock.isLocked()) {
                lock.unlock();
            }
        }

    }

    @Override
    public <T> tryLock(DistributedLock<T> distributedLock, boolean fairLock) {
        return tryLock(distributedLock, DEFAULT_TRY_OUT_TIME, DEFAULT_OUT_TIME, DEFAULT_TIME_UNIT, fairLock);
    }

    @Override
    public <T> tryLock(DistributedLock<T> distributedLock, long tryOutTime, long outTime, TimeUnit timeUnit, boolean fairLock) {
        RLock lock = getLock(distributedLock.lockName(), fairLock);
        try {
            if (lock.tryLock(tryOutTime, outTime, timeUnit)) {
                return distributedLock.process();
            }
        } catch (InterruptedException ignored) {
        } finally {
            if (lock != null && lock.isLocked()) {
                lock.unlock();
            }
        }
        return null;
    }

    private RLock getLock(String lockName, boolean fairLock) {
        return fairLock ? redisson.getFairLock(lockName) : redisson.getLock(lockName);
    }

}

定义切面

  • CiderDistributedLockAspect.java
package com.shopcider.plutus.component.aop;

import com.shopcider.plutus.component.annotation.CiderDistributedLock;
import com.shopcider.plutus.component.lock.DistributedLock;
import com.shopcider.plutus.component.lock.DistributedLockTemplate;
import com.shopcider.plutus.component.util.DataUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author : wh
 * @date : 2021/7/18 12:52
 * @description: 分布式锁切面
 */

@Aspect
@Component
public class CiderDistributedLockAspect {

    @Autowired
    private DistributedLockTemplate lock;

    @Pointcut("@annotation(com.shopcider.plutus.component.annotation.CiderDistributedLock)")
    public void ciderDistributedLockAspect() {

    }

    @Around(value = "ciderDistributedLockAspect()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {

        //切点所在的类
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Method method = methodSignature.getMethod();
        //方法参数
        Object[] args = pjp.getArgs();
        CiderDistributedLock annotation = method.getAnnotation(CiderDistributedLock.class);
        String lockName = getLockName(annotation, args, method);
        boolean fairLock = annotation.fairLock();
        return lock.tryLock(new DistributedLock<Object>() {
            @Override
            public Object process() {
                return proceed(pjp);
            }

            @Override
            public String lockName() {
                return lockName;
            }
        }, annotation.outTime(), annotation.tryWaitTime(), annotation.timeUnit(), fairLock);
    }

    public Object proceed(ProceedingJoinPoint pjp) {
        try {
            return pjp.proceed();
        } catch (Throwable throwable) {
            throw new RuntimeException(throwable);
        }
    }


    /**
     * 获取锁名字,优先获取注解中锁名
     *
     * @param ciderDistributedLock
     * @param args
     * @param method
     * @return
     */

    private String getLockName(CiderDistributedLock ciderDistributedLock, Object[] args, Method method) {
        if (DataUtils.isNotEmpty(ciderDistributedLock.lockName())) {
            return ciderDistributedLock.lockName();
        }
        String lockNamePre = ciderDistributedLock.lockNamePre();
        String lockNamePost = ciderDistributedLock.lockNamePost();
        String separator = ciderDistributedLock.separator();

        String preExpression = parseExpression(lockNamePre, method, args);
        String postExpression = parseExpression(lockNamePost, method, args);
        StringBuilder sb = new StringBuilder();
        if (DataUtils.isNotEmpty(preExpression)) {
            sb.append(preExpression);
        } else {
            sb.append(lockNamePre);
        }
        sb.append(separator);
        if (DataUtils.isNotEmpty(postExpression)) {
            sb.append(postExpression);
        } else {
            sb.append(lockNamePost);
        }
        return sb.toString();
    }

    /**
     * el表达式解析
     *
     * @param expressionString 解析值
     * @param method           方法
     * @param args             参数
     * @return
     */

    private String parseExpression(String expressionString, Method method, Object[] args) {
        //获取被拦截方法参数名列表
        LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
        String[] paramNameArr = discoverer.getParameterNames(method);
        //SPEL解析
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < Objects.requireNonNull(paramNameArr).length; i++) {
            context.setVariable(paramNameArr[i], args[i]);
        }
        return parser.parseExpression(expressionString).getValue(context, String.class);
    }


}

测试

controller

public void getList(WarehouseReturnDTO warehouseReturnDTO) {
        return warehouseQueryApplicationService.getReturnList(warehouseReturnDTO);
    }

private int i = 10;

@CiderDistributedLock(lockNamePre = "#query.userName")
public void getReturnList(WarehouseReturnDTO query) {
        i = i + 1;
        System.out.println(i);
}

这里测试方法很简单就是定义一个全局变量i,没有使用数据库,然后就对i进行累加(这里只是作了单机的测试)

然后我们使用jmeter设置30个线程进行并发调用测试(jmeter教程)

springboot之Redisson使用及使用Aop优化注解实现
在这里插入图片描述

看看没加@CiderDistributedLock注解前的结果:springboot之Redisson使用及使用Aop优化注解实现

加了注解后的结果:springboot之Redisson使用及使用Aop优化注解实现

后续需要源码可以留言上传github参考


原文始发于微信公众号(小奏技术):springboot之Redisson使用及使用Aop优化注解实现

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/29730.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!