소스 검색

starter-搭建Redisson分布式锁模块-happy-cloud-starter-lock

ZhangWenQiang 4 년 전
부모
커밋
00969a6058
22개의 변경된 파일1124개의 추가작업 그리고 6개의 파일을 삭제
  1. 39 0
      happy-cloud-starter/happy-cloud-starter-lock/pom.xml
  2. 57 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/annotation/JLock.java
  3. 36 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/annotation/JRepeat.java
  4. 72 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/annotation/LockConstant.java
  5. 67 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/aspect/BaseAspect.java
  6. 167 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/aspect/DistributedLockHandler.java
  7. 80 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/aspect/RepeatSubmitAspect.java
  8. 126 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/client/RedissonLockClient.java
  9. 36 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/config/RedissonConfiguration.java
  10. 100 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/core/RedissonManager.java
  11. 21 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/core/strategy/RedissonConfigStrategy.java
  12. 43 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/core/strategy/impl/ClusterRedissonConfigStrategyImpl.java
  13. 54 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/core/strategy/impl/MasterslaveRedissonConfigStrategyImpl.java
  14. 47 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/core/strategy/impl/SentinelRedissonConfigStrategyImpl.java
  15. 40 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/core/strategy/impl/StandaloneRedissonConfigStrategyImpl.java
  16. 17 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/enums/GlobalConstant.java
  17. 22 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/enums/LockModel.java
  18. 39 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/enums/RedisConnectionType.java
  19. 39 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/prop/RedissonProperties.java
  20. 4 0
      happy-cloud-starter/happy-cloud-starter-lock/src/main/resources/META-INF/spring.factories
  21. 1 0
      happy-cloud-starter/pom.xml
  22. 17 6
      pom.xml

+ 39 - 0
happy-cloud-starter/happy-cloud-starter-lock/pom.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>happy-cloud-starter</artifactId>
+        <groupId>org.happyframework.cloud</groupId>
+        <version>2.2.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>happy-cloud-starter-lock</artifactId>
+    <version>2.2.0</version>
+    <description>happy-cloud-starter-分布式锁</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.redisson</groupId>
+            <artifactId>redisson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 57 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/annotation/JLock.java

@@ -0,0 +1,57 @@
+package org.jeecg.boot.starter.lock.annotation;
+
+import org.jeecg.boot.starter.lock.enums.LockModel;
+
+import java.lang.annotation.*;
+
+/**
+ * Redisson分布式锁注解
+ *
+ * @author zyf
+ * @date 2020-11-11
+ */
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface JLock {
+
+    /**
+     * 锁的模式:如果不设置,自动模式,当参数只有一个.使用 REENTRANT 参数多个 MULTIPLE
+     */
+    LockModel lockModel() default LockModel.AUTO;
+
+    /**
+     * 如果keys有多个,如果不设置,则使用 联锁
+     * @return
+     */
+    String[] lockKey() default {};
+
+    /**
+     * key的静态常量:当key的spel的值是LIST,数组时使用+号连接将会被spel认为这个变量是个字符串
+     * @return
+     */
+    String keyConstant() default "";
+
+
+    /**
+     * 锁超时时间,默认30000毫秒
+     *
+     * @return int
+     */
+    long expireSeconds() default 30000L;
+
+    /**
+     * 等待加锁超时时间,默认10000毫秒 -1 则表示一直等待
+     *
+     * @return int
+     */
+     long waitTime() default 10000L;
+
+    /**
+     * 未取到锁时提示信息
+     *
+     * @return
+     */
+    String failMsg() default "获取锁失败,请稍后重试";
+}

+ 36 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/annotation/JRepeat.java

@@ -0,0 +1,36 @@
+package org.jeecg.boot.starter.lock.annotation;
+
+/**
+ * @author zyf
+ */
+
+import java.lang.annotation.*;
+
+/**
+ * 防止重复提交的注解
+ *
+ * @author 2019年6月18日
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+@Documented
+public @interface JRepeat {
+
+    /**
+     * 超时时间
+     *
+     * @return
+     */
+    int lockTime();
+
+
+    /**
+     * redis 锁key的
+     *
+     * @return redis 锁key
+     */
+    String lockKey() default "";
+
+
+
+}

+ 72 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/annotation/LockConstant.java

@@ -0,0 +1,72 @@
+package org.jeecg.boot.starter.lock.annotation;
+
+/**
+ * @author zyf
+ * @date 2019/10/26 18:26
+ */
+
+/**
+ * 分布式锁枚举类
+ * @author zyf
+ */
+public enum LockConstant {
+    /**
+     * 通用锁常量
+     */
+    COMMON("commonLock:", 1, 500, "请勿重复点击");
+    /**
+     * 分布式锁前缀
+     */
+    private String keyPrefix;
+    /**
+     * 等到最大时间,强制获取锁
+     */
+    private int waitTime;
+    /**
+     * 锁失效时间
+     */
+    private int leaseTime;
+    /**
+     * 加锁提示
+     */
+    private String message;
+
+    LockConstant(String keyPrefix, int waitTime, int leaseTime, String message) {
+        this.keyPrefix = keyPrefix;
+        this.waitTime = waitTime;
+        this.leaseTime = leaseTime;
+        this.message = message;
+    }
+
+    public String getKeyPrefix() {
+        return keyPrefix;
+    }
+
+    public void setKeyPrefix(String keyPrefix) {
+        this.keyPrefix = keyPrefix;
+    }
+
+    public int getWaitTime() {
+        return waitTime;
+    }
+
+    public void setWaitTime(int waitTime) {
+        this.waitTime = waitTime;
+    }
+
+    public int getLeaseTime() {
+        return leaseTime;
+    }
+
+    public void setLeaseTime(int leaseTime) {
+        this.leaseTime = leaseTime;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+}

+ 67 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/aspect/BaseAspect.java

@@ -0,0 +1,67 @@
+package org.jeecg.boot.starter.lock.aspect;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author zyf
+ */
+@Slf4j
+public class BaseAspect {
+
+    /**
+     * 通过spring SpEL 获取参数
+     *
+     * @param key            定义的key值 以#开头 例如:#user
+     * @param parameterNames 形参
+     * @param values         形参值
+     * @param keyConstant    key的常亮
+     * @return
+     */
+    public List<String> getValueBySpEL(String key, String[] parameterNames, Object[] values, String keyConstant) {
+        List<String> keys = new ArrayList<>();
+        if (!key.contains("#")) {
+            String s = "redis:lock:" + key + keyConstant;
+            log.info("lockKey:" + s);
+            keys.add(s);
+            return keys;
+        }
+        //spel解析器
+        ExpressionParser parser = new SpelExpressionParser();
+        //spel上下文
+        EvaluationContext context = new StandardEvaluationContext();
+        for (int i = 0; i < parameterNames.length; i++) {
+            context.setVariable(parameterNames[i], values[i]);
+        }
+        Expression expression = parser.parseExpression(key);
+        Object value = expression.getValue(context);
+        if (value != null) {
+            if (value instanceof List) {
+                List value1 = (List) value;
+                for (Object o : value1) {
+                    addKeys(keys, o, keyConstant);
+                }
+            } else if (value.getClass().isArray()) {
+                Object[] obj = (Object[]) value;
+                for (Object o : obj) {
+                    addKeys(keys, o, keyConstant);
+                }
+            } else {
+                addKeys(keys, value, keyConstant);
+            }
+        }
+        log.info("表达式key={},value={}", key, keys);
+        return keys;
+    }
+
+    private void addKeys(List<String> keys, Object o, String keyConstant) {
+        keys.add("redis:lock:" + o.toString() + keyConstant);
+    }
+}

+ 167 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/aspect/DistributedLockHandler.java

@@ -0,0 +1,167 @@
+package org.jeecg.boot.starter.lock.aspect;
+
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.jeecg.boot.starter.lock.annotation.JLock;
+import org.jeecg.boot.starter.lock.enums.LockModel;
+import org.redisson.RedissonMultiLock;
+import org.redisson.RedissonRedLock;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.Expression;
+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.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * 分布式锁解析器
+ *
+ * @author zyf
+ * @date 2020-11-11
+ */
+@Slf4j
+@Aspect
+@Component
+public class DistributedLockHandler extends BaseAspect{
+
+
+    @Autowired(required = false)
+    private RedissonClient redissonClient;
+
+    /**
+     * 切面环绕通知
+     *
+     * @param joinPoint
+     * @param jLock
+     * @return Object
+     */
+    @SneakyThrows
+    @Around("@annotation(jLock)")
+    public Object around(ProceedingJoinPoint joinPoint, JLock jLock) {
+        Object obj = null;
+        log.info("进入RedisLock环绕通知...");
+        RLock rLock = getLock(joinPoint, jLock);
+        boolean res = false;
+        //获取超时时间
+        long expireSeconds = jLock.expireSeconds();
+        //等待多久,n秒内获取不到锁,则直接返回
+        long waitTime = jLock.waitTime();
+        //执行aop
+        if (rLock != null) {
+            try {
+                if (waitTime == -1) {
+                    res = true;
+                    //一直等待加锁
+                    rLock.lock(expireSeconds, TimeUnit.MILLISECONDS);
+                } else {
+                    res = rLock.tryLock(waitTime, expireSeconds, TimeUnit.MILLISECONDS);
+                }
+                if (res) {
+                    obj = joinPoint.proceed();
+                } else {
+                    log.error("获取锁异常");
+                }
+            } finally {
+                if (res) {
+                    rLock.unlock();
+                }
+            }
+        }
+        log.info("结束RedisLock环绕通知...");
+        return obj;
+    }
+
+    @SneakyThrows
+    private RLock getLock(ProceedingJoinPoint joinPoint, JLock jLock) {
+        String[] keys = jLock.lockKey();
+        if (keys.length == 0) {
+            throw new RuntimeException("keys不能为空");
+        }
+        String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod());
+        Object[] args = joinPoint.getArgs();
+
+        LockModel lockModel = jLock.lockModel();
+        if (!lockModel.equals(LockModel.MULTIPLE) && !lockModel.equals(LockModel.REDLOCK) && keys.length > 1) {
+            throw new RuntimeException("参数有多个,锁模式为->" + lockModel.name() + ".无法锁定");
+        }
+        RLock rLock = null;
+        String keyConstant = jLock.keyConstant();
+        if (lockModel.equals(LockModel.AUTO)) {
+            if (keys.length > 1) {
+                lockModel = LockModel.REDLOCK;
+            } else {
+                lockModel = LockModel.REENTRANT;
+            }
+        }
+        switch (lockModel) {
+            case FAIR:
+                rLock = redissonClient.getFairLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0));
+                break;
+            case REDLOCK:
+                List<RLock> rLocks = new ArrayList<>();
+                for (String key : keys) {
+                    List<String> valueBySpEL = getValueBySpEL(key, parameterNames, args, keyConstant);
+                    for (String s : valueBySpEL) {
+                        rLocks.add(redissonClient.getLock(s));
+                    }
+                }
+                RLock[] locks = new RLock[rLocks.size()];
+                int index = 0;
+                for (RLock r : rLocks) {
+                    locks[index++] = r;
+                }
+                rLock = new RedissonRedLock(locks);
+                break;
+            case MULTIPLE:
+                rLocks = new ArrayList<>();
+
+                for (String key : keys) {
+                    List<String> valueBySpEL = getValueBySpEL(key, parameterNames, args, keyConstant);
+                    for (String s : valueBySpEL) {
+                        rLocks.add(redissonClient.getLock(s));
+                    }
+                }
+                locks = new RLock[rLocks.size()];
+                index = 0;
+                for (RLock r : rLocks) {
+                    locks[index++] = r;
+                }
+                rLock = new RedissonMultiLock(locks);
+                break;
+            case REENTRANT:
+                List<String> valueBySpEL = getValueBySpEL(keys[0], parameterNames, args, keyConstant);
+                //如果spel表达式是数组或者LIST 则使用红锁
+                if (valueBySpEL.size() == 1) {
+                    rLock = redissonClient.getLock(valueBySpEL.get(0));
+                } else {
+                    locks = new RLock[valueBySpEL.size()];
+                    index = 0;
+                    for (String s : valueBySpEL) {
+                        locks[index++] = redissonClient.getLock(s);
+                    }
+                    rLock = new RedissonRedLock(locks);
+                }
+                break;
+            case READ:
+                rLock = redissonClient.getReadWriteLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0)).readLock();
+                break;
+            case WRITE:
+                rLock = redissonClient.getReadWriteLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0)).writeLock();
+                break;
+        }
+        return rLock;
+    }
+}

+ 80 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/aspect/RepeatSubmitAspect.java

@@ -0,0 +1,80 @@
+package org.jeecg.boot.starter.lock.aspect;
+
+/**
+ * @author zyf
+ */
+
+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.jeecg.boot.starter.lock.annotation.JRepeat;
+import org.jeecg.boot.starter.lock.client.RedissonLockClient;
+import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 防止重复提交分布式锁拦截器
+ *
+ * @author 2019年6月18日
+ */
+@Aspect
+@Component
+public class RepeatSubmitAspect extends BaseAspect{
+
+    @Resource
+    private RedissonLockClient redissonLockClient;
+
+    /***
+     * 定义controller切入点拦截规则,拦截JRepeat注解的业务方法
+     */
+    @Pointcut("@annotation(jRepeat)")
+    public void pointCut(JRepeat jRepeat) {
+    }
+
+    /**
+     * AOP分布式锁拦截
+     *
+     * @param joinPoint
+     * @return
+     * @throws Exception
+     */
+    @Around("pointCut(jRepeat)")
+    public Object repeatSubmit(ProceedingJoinPoint joinPoint,JRepeat jRepeat) throws Throwable {
+        String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod());
+        if (Objects.nonNull(jRepeat)) {
+            // 获取参数
+            Object[] args = joinPoint.getArgs();
+            // 进行一些参数的处理,比如获取订单号,操作人id等
+            StringBuffer lockKeyBuffer = new StringBuffer();
+            String key =getValueBySpEL(jRepeat.lockKey(), parameterNames, args,"RepeatSubmit").get(0);
+            // 公平加锁,lockTime后锁自动释放
+            boolean isLocked = false;
+            try {
+                isLocked = redissonLockClient.fairLock(key, TimeUnit.SECONDS, jRepeat.lockTime());
+                // 如果成功获取到锁就继续执行
+                if (isLocked) {
+                    // 执行进程
+                    return joinPoint.proceed();
+                } else {
+                    // 未获取到锁
+                    throw new Exception("请勿重复提交");
+                }
+            } finally {
+                // 如果锁还存在,在方法执行完成后,释放锁
+                if (isLocked) {
+                    redissonLockClient.unlock(key);
+                }
+            }
+        }
+
+        return joinPoint.proceed();
+    }
+
+
+}

+ 126 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/client/RedissonLockClient.java

@@ -0,0 +1,126 @@
+package org.jeecg.boot.starter.lock.client;
+
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 分布式锁实现基于Redisson
+ *
+ * @author zyf
+ * @date 2020-11-11
+ */
+@Slf4j
+@Component
+public class RedissonLockClient {
+
+    @Autowired
+    private RedissonClient redissonClient;
+
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+
+    /**
+     * 获取锁
+     */
+    public RLock getLock(String lockKey) {
+        return redissonClient.getLock(lockKey);
+    }
+
+    /**
+     * 加锁操作
+     *
+     * @return boolean
+     */
+    public boolean tryLock(String lockName, long expireSeconds) {
+        return tryLock(lockName, 0, expireSeconds);
+    }
+
+
+    /**
+     * 加锁操作
+     *
+     * @return boolean
+     */
+    public boolean tryLock(String lockName, long waitTime, long expireSeconds) {
+        RLock rLock = getLock(lockName);
+        boolean getLock = false;
+        try {
+            getLock = rLock.tryLock(waitTime, expireSeconds, TimeUnit.SECONDS);
+            if (getLock) {
+                log.info("获取锁成功,lockName={}", lockName);
+            } else {
+                log.info("获取锁失败,lockName={}", lockName);
+            }
+        } catch (InterruptedException e) {
+            log.error("获取式锁异常,lockName=" + lockName, e);
+            getLock = false;
+        }
+        return getLock;
+    }
+
+
+    public boolean fairLock(String lockKey, TimeUnit unit, int leaseTime) {
+        RLock fairLock = redissonClient.getFairLock(lockKey);
+        try {
+            boolean existKey = existKey(lockKey);
+            // 已经存在了,就直接返回
+            if (existKey) {
+                return false;
+            }
+            return fairLock.tryLock(3, leaseTime, unit);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    public boolean existKey(String key) {
+        return redisTemplate.hasKey(key);
+    }
+    /**
+     * 锁lockKey
+     *
+     * @param lockKey
+     * @return
+     */
+    public RLock lock(String lockKey) {
+        RLock lock = getLock(lockKey);
+        lock.lock();
+        return lock;
+    }
+
+    /**
+     * 锁lockKey
+     *
+     * @param lockKey
+     * @param leaseTime
+     * @return
+     */
+    public RLock lock(String lockKey, long leaseTime) {
+        RLock lock = getLock(lockKey);
+        lock.lock(leaseTime, TimeUnit.SECONDS);
+        return lock;
+    }
+
+
+    /**
+     * 解锁
+     *
+     * @param lockName 锁名称
+     */
+    public void unlock(String lockName) {
+        try {
+            redissonClient.getLock(lockName).unlock();
+        } catch (Exception e) {
+            log.error("解锁异常,lockName=" + lockName, e);
+        }
+    }
+
+
+}

+ 36 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/config/RedissonConfiguration.java

@@ -0,0 +1,36 @@
+package org.jeecg.boot.starter.lock.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.boot.starter.lock.core.RedissonManager;
+import org.jeecg.boot.starter.lock.prop.RedissonProperties;
+import org.redisson.api.RedissonClient;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+
+/**
+ * Redisson自动化配置
+ *
+ * @author zyf
+ * @date 2020-11-11
+ */
+@Slf4j
+@Configuration
+@ConditionalOnClass(RedissonProperties.class)
+@EnableConfigurationProperties(RedissonProperties.class)
+public class RedissonConfiguration {
+
+
+
+	@Bean
+	@ConditionalOnMissingBean(RedissonClient.class)
+	public RedissonClient redissonClient(RedissonProperties redissonProperties) {
+		RedissonManager redissonManager = new RedissonManager(redissonProperties);
+		log.info("RedissonManager初始化完成,当前连接方式:" + redissonProperties.getType() + ",连接地址:" + redissonProperties.getAddress());
+		return redissonManager.getRedisson();
+	}
+
+}

+ 100 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/core/RedissonManager.java

@@ -0,0 +1,100 @@
+package org.jeecg.boot.starter.lock.core;
+
+
+import com.google.common.base.Preconditions;
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.boot.starter.lock.core.strategy.RedissonConfigStrategy;
+import org.jeecg.boot.starter.lock.prop.RedissonProperties;
+import org.jeecg.boot.starter.lock.core.strategy.impl.ClusterRedissonConfigStrategyImpl;
+import org.jeecg.boot.starter.lock.core.strategy.impl.MasterslaveRedissonConfigStrategyImpl;
+import org.jeecg.boot.starter.lock.core.strategy.impl.SentinelRedissonConfigStrategyImpl;
+import org.jeecg.boot.starter.lock.core.strategy.impl.StandaloneRedissonConfigStrategyImpl;
+import org.jeecg.boot.starter.lock.enums.RedisConnectionType;
+import org.redisson.Redisson;
+import org.redisson.config.Config;
+
+
+/**
+ * Redisson配置管理器,用于初始化的redisson实例
+ *
+ * @author zyf
+ * @date 2020-11-12
+ */
+@Slf4j
+public class RedissonManager {
+
+    private Config config = new Config();
+
+    private Redisson redisson = null;
+
+    public RedissonManager() {
+    }
+
+    public RedissonManager(RedissonProperties redissonProperties) {
+        //装配开关
+        Boolean enabled = redissonProperties.getEnabled();
+        if (enabled) {
+            try {
+                config = RedissonConfigFactory.getInstance().createConfig(redissonProperties);
+                redisson = (Redisson) Redisson.create(config);
+            } catch (Exception e) {
+                log.error("Redisson初始化错误", e);
+            }
+        }
+    }
+
+    public Redisson getRedisson() {
+        return redisson;
+    }
+
+    /**
+     * Redisson连接方式配置工厂
+     * 双重检查锁
+     */
+    static class RedissonConfigFactory {
+
+        private RedissonConfigFactory() {
+        }
+
+        private static volatile RedissonConfigFactory factory = null;
+
+        public static RedissonConfigFactory getInstance() {
+            if (factory == null) {
+                synchronized (Object.class) {
+                    if (factory == null) {
+                        factory = new RedissonConfigFactory();
+                    }
+                }
+            }
+            return factory;
+        }
+
+        /**
+         * 根据连接类型創建连接方式的配置
+         *
+         * @param redissonProperties
+         * @return Config
+         */
+        Config createConfig(RedissonProperties redissonProperties) {
+            Preconditions.checkNotNull(redissonProperties);
+            Preconditions.checkNotNull(redissonProperties.getAddress(), "redis地址未配置");
+            RedisConnectionType connectionType = redissonProperties.getType();
+            // 声明连接方式
+            RedissonConfigStrategy redissonConfigStrategy;
+            if (connectionType.equals(RedisConnectionType.SENTINEL)) {
+                redissonConfigStrategy = new SentinelRedissonConfigStrategyImpl();
+            } else if (connectionType.equals(RedisConnectionType.CLUSTER)) {
+                redissonConfigStrategy = new ClusterRedissonConfigStrategyImpl();
+            } else if (connectionType.equals(RedisConnectionType.MASTERSLAVE)) {
+                redissonConfigStrategy = new MasterslaveRedissonConfigStrategyImpl();
+            } else {
+                redissonConfigStrategy = new StandaloneRedissonConfigStrategyImpl();
+            }
+            Preconditions.checkNotNull(redissonConfigStrategy, "连接方式创建异常");
+
+            return redissonConfigStrategy.createRedissonConfig(redissonProperties);
+        }
+    }
+
+
+}

+ 21 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/core/strategy/RedissonConfigStrategy.java

@@ -0,0 +1,21 @@
+package org.jeecg.boot.starter.lock.core.strategy;
+
+import org.jeecg.boot.starter.lock.prop.RedissonProperties;
+import org.redisson.config.Config;
+
+/**
+ * Redisson配置构建接口
+ *
+ * @author zyf
+ * @date 2020-11-11
+ */
+public interface RedissonConfigStrategy {
+
+	/**
+	 * 根据不同的Redis配置策略创建对应的Config
+	 *
+	 * @param redissonProperties
+	 * @return Config
+	 */
+	Config createRedissonConfig(RedissonProperties redissonProperties);
+}

+ 43 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/core/strategy/impl/ClusterRedissonConfigStrategyImpl.java

@@ -0,0 +1,43 @@
+package org.jeecg.boot.starter.lock.core.strategy.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.boot.starter.lock.core.strategy.RedissonConfigStrategy;
+import org.jeecg.boot.starter.lock.prop.RedissonProperties;
+import org.jeecg.boot.starter.lock.enums.GlobalConstant;
+import org.redisson.config.Config;
+
+
+/**
+ * 集群方式Redisson配置
+ * cluster方式至少6个节点(3主3从)
+ * 配置方式:127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383,127.0.0.1:6384
+ *
+ * @author zyf
+ * @date 2020-11-11
+ */
+@Slf4j
+public class ClusterRedissonConfigStrategyImpl implements RedissonConfigStrategy {
+
+    @Override
+    public Config createRedissonConfig(RedissonProperties redissonProperties) {
+        Config config = new Config();
+        try {
+            String address = redissonProperties.getAddress();
+            String password = redissonProperties.getPassword();
+            String[] addrTokens = address.split(",");
+            // 设置集群(cluster)节点的服务IP和端口
+            for (int i = 0; i < addrTokens.length; i++) {
+                config.useClusterServers().addNodeAddress(GlobalConstant.REDIS_CONNECTION_PREFIX + addrTokens[i]);
+                if (StringUtils.isNotBlank(password)) {
+                    config.useClusterServers().setPassword(password);
+                }
+            }
+            log.info("初始化集群方式Config,连接地址:" + address);
+        } catch (Exception e) {
+            log.error("集群Redisson初始化错误", e);
+            e.printStackTrace();
+        }
+        return config;
+    }
+}

+ 54 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/core/strategy/impl/MasterslaveRedissonConfigStrategyImpl.java

@@ -0,0 +1,54 @@
+package org.jeecg.boot.starter.lock.core.strategy.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.boot.starter.lock.core.strategy.RedissonConfigStrategy;
+import org.jeecg.boot.starter.lock.prop.RedissonProperties;
+import org.jeecg.boot.starter.lock.enums.GlobalConstant;
+import org.redisson.config.Config;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 主从方式Redisson配置
+ * <p>配置方式: 127.0.0.1:6379(主),127.0.0.1:6380(子),127.0.0.1:6381(子)</p>
+ *
+ * @author zyf
+ * @date 2020-11-11
+ */
+@Slf4j
+public class MasterslaveRedissonConfigStrategyImpl implements RedissonConfigStrategy {
+
+	@Override
+	public Config createRedissonConfig(RedissonProperties redissonProperties) {
+		Config config = new Config();
+		try {
+			String address = redissonProperties.getAddress();
+			String password = redissonProperties.getPassword();
+			int database = redissonProperties.getDatabase();
+			String[] addrTokens = address.split(",");
+			String masterNodeAddr = addrTokens[0];
+			// 设置主节点ip
+			config.useMasterSlaveServers().setMasterAddress(masterNodeAddr);
+			if (StringUtils.isNotBlank(password)) {
+				config.useMasterSlaveServers().setPassword(password);
+			}
+			config.useMasterSlaveServers().setDatabase(database);
+			// 设置从节点,移除第一个节点,默认第一个为主节点
+			List<String> slaveList = new ArrayList<>();
+			for (String addrToken : addrTokens) {
+				slaveList.add(GlobalConstant.REDIS_CONNECTION_PREFIX + addrToken);
+			}
+			slaveList.remove(0);
+
+			config.useMasterSlaveServers().addSlaveAddress((String[]) slaveList.toArray());
+			log.info("初始化主从方式Config,redisAddress:" + address);
+		} catch (Exception e) {
+			log.error("主从Redisson初始化错误", e);
+			e.printStackTrace();
+		}
+		return config;
+	}
+}

+ 47 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/core/strategy/impl/SentinelRedissonConfigStrategyImpl.java

@@ -0,0 +1,47 @@
+package org.jeecg.boot.starter.lock.core.strategy.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.boot.starter.lock.core.strategy.RedissonConfigStrategy;
+import org.jeecg.boot.starter.lock.prop.RedissonProperties;
+import org.jeecg.boot.starter.lock.enums.GlobalConstant;
+import org.redisson.config.Config;
+
+
+/**
+ * 哨兵方式Redis连接配置
+ * 比如sentinel.conf里配置为sentinel monitor my-sentinel-name 127.0.0.1 6379 2,那么这里就配置my-sentinel-name
+ * 配置方式:my-sentinel-name,127.0.0.1:26379,127.0.0.1:26389,127.0.0.1:26399
+ * @author zyf
+ * @date 2020-11-11
+ */
+@Slf4j
+public class SentinelRedissonConfigStrategyImpl implements RedissonConfigStrategy {
+
+	@Override
+	public Config createRedissonConfig(RedissonProperties redissonProperties) {
+		Config config = new Config();
+		try {
+			String address = redissonProperties.getAddress();
+			String password = redissonProperties.getPassword();
+			int database = redissonProperties.getDatabase();
+			String[] addrTokens = address.split(",");
+			String sentinelAliasName = addrTokens[0];
+			// 设置redis配置文件sentinel.conf配置的sentinel别名
+			config.useSentinelServers().setMasterName(sentinelAliasName);
+			config.useSentinelServers().setDatabase(database);
+			if (StringUtils.isNotBlank(password)) {
+				config.useSentinelServers().setPassword(password);
+			}
+			// 设置哨兵节点的服务IP和端口
+			for (int i = 1; i < addrTokens.length; i++) {
+				config.useSentinelServers().addSentinelAddress(GlobalConstant.REDIS_CONNECTION_PREFIX+ addrTokens[i]);
+			}
+			log.info("初始化哨兵方式Config,redisAddress:" + address);
+		} catch (Exception e) {
+			log.error("哨兵Redisson初始化错误", e);
+			e.printStackTrace();
+		}
+		return config;
+	}
+}

+ 40 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/core/strategy/impl/StandaloneRedissonConfigStrategyImpl.java

@@ -0,0 +1,40 @@
+package org.jeecg.boot.starter.lock.core.strategy.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.jeecg.boot.starter.lock.core.strategy.RedissonConfigStrategy;
+import org.jeecg.boot.starter.lock.prop.RedissonProperties;
+import org.jeecg.boot.starter.lock.enums.GlobalConstant;
+import org.redisson.config.Config;
+
+
+/**
+ * 单机方式Redisson配置
+ *
+ * @author zyf
+ * @date 2020-11-11
+ */
+@Slf4j
+public class StandaloneRedissonConfigStrategyImpl implements RedissonConfigStrategy {
+
+    @Override
+    public Config createRedissonConfig(RedissonProperties redissonProperties) {
+        Config config = new Config();
+        try {
+            String address = redissonProperties.getAddress();
+            String password = redissonProperties.getPassword();
+            int database = redissonProperties.getDatabase();
+            String redisAddr = GlobalConstant.REDIS_CONNECTION_PREFIX + address;
+            config.useSingleServer().setAddress(redisAddr);
+            config.useSingleServer().setDatabase(database);
+            if (StringUtils.isNotBlank(password)) {
+                config.useSingleServer().setPassword(password);
+            }
+            log.info("初始化Redisson单机配置,连接地址:" + address);
+        } catch (Exception e) {
+            log.error("单机Redisson初始化错误", e);
+            e.printStackTrace();
+        }
+        return config;
+    }
+}

+ 17 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/enums/GlobalConstant.java

@@ -0,0 +1,17 @@
+package org.jeecg.boot.starter.lock.enums;
+
+/**
+ * 全局常量枚举
+ *
+ * @author zyf
+ * @date 2020-11-11
+ */
+
+public interface GlobalConstant {
+
+    /**
+     * Redis地址连接前缀
+     */
+    String REDIS_CONNECTION_PREFIX = "redis://";
+
+}

+ 22 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/enums/LockModel.java

@@ -0,0 +1,22 @@
+package org.jeecg.boot.starter.lock.enums;
+
+/**
+ * 锁的模式
+ * @author jeecg
+ */
+public enum LockModel {
+    //可重入锁
+    REENTRANT,
+    //公平锁
+    FAIR,
+    //联锁(可以把一组锁当作一个锁来加锁和释放)
+    MULTIPLE,
+    //红锁
+    REDLOCK,
+    //读锁
+    READ,
+    //写锁
+    WRITE,
+    //自动模式,当参数只有一个.使用 REENTRANT 参数多个 REDLOCK
+    AUTO
+}

+ 39 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/enums/RedisConnectionType.java

@@ -0,0 +1,39 @@
+package org.jeecg.boot.starter.lock.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * Redis连接方式
+ * @author zyf
+ * @date 2020-11-11
+ */
+@Getter
+@AllArgsConstructor
+public enum RedisConnectionType {
+	/**
+	 * 单机部署方式(默认)
+	 */
+	STANDALONE("standalone", "单机部署方式"),
+	/**
+	 * 哨兵部署方式
+	 */
+	SENTINEL("sentinel", "哨兵部署方式"),
+	/**
+	 * 集群部署方式
+	 */
+	CLUSTER("cluster", "集群方式"),
+	/**
+	 * 主从部署方式
+	 */
+	MASTERSLAVE("masterslave", "主从部署方式");
+
+	/**
+	 * 编码
+	 */
+	private final String code;
+	/**
+	 * 名称
+	 */
+	private final String name;
+}

+ 39 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/java/org/jeecg/boot/starter/lock/prop/RedissonProperties.java

@@ -0,0 +1,39 @@
+package org.jeecg.boot.starter.lock.prop;
+
+import lombok.Data;
+import org.jeecg.boot.starter.lock.enums.RedisConnectionType;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Redisson配置映射类
+ *
+ * @author zyf
+ * @date 2020-11-11
+ */
+@Data
+@ConfigurationProperties(prefix = "jeecg.redisson")
+public class RedissonProperties {
+
+    /**
+     * redis主机地址,ip:port,多个用逗号(,)分隔
+     */
+    private String address;
+    /**
+     * 连接类型
+     */
+    private RedisConnectionType type;
+    /**
+     * 密码
+     */
+    private String password;
+    /**
+     * 数据库(默认0)
+     */
+    private int database;
+
+    /**
+     * 是否装配redisson配置
+     */
+    private Boolean enabled = true;
+
+}

+ 4 - 0
happy-cloud-starter/happy-cloud-starter-lock/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,4 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  org.jeecg.boot.starter.lock.config.RedissonConfiguration
+
+

+ 1 - 0
happy-cloud-starter/pom.xml

@@ -19,6 +19,7 @@
 
     <modules>
         <module>happy-cloud-starter-rabbitmq</module>
+        <module>happy-cloud-starter-lock</module>
     </modules>
 
     <dependencies>

+ 17 - 6
pom.xml

@@ -67,12 +67,14 @@
         <mybatis-plus.version>3.1.2</mybatis-plus.version>
         <druid.version>1.1.17</druid.version>
         <commons.version>2.6</commons.version>
+		<fastjson.version>1.2.78</fastjson.version>
         <aliyun-java-sdk-core.version>3.2.3</aliyun-java-sdk-core.version>
 		<aliyun-java-sdk-dysmsapi.version>1.0.0</aliyun-java-sdk-dysmsapi.version>
 		<aliyun.oss.version>3.6.0</aliyun.oss.version>
 		<guava.version>26.0-jre</guava.version>
 		<shiro.version>1.7.0</shiro.version>
 		<java-jwt.version>3.11.0</java-jwt.version>
+		<redisson.version>3.16.1</redisson.version>
 		<shiro-redis.version>3.1.0</shiro-redis.version>
 		<swagger2.version>2.9.2</swagger2.version>
 		<knife4j.version>2.0.3</knife4j.version>
@@ -159,7 +161,7 @@
 		<dependency>
          	<groupId>com.alibaba</groupId>
          	<artifactId>fastjson</artifactId>
-         	<version>1.2.69</version>
+         	<version>${fastjson.version}</version>
         </dependency>
       
 		<!--mysql-->
@@ -290,11 +292,6 @@
 				</exclusion>
 			</exclusions>
 		</dependency>
-		<dependency>
-			<groupId>com.google.guava</groupId>
-			<artifactId>guava</artifactId>
-			<version>${guava.version}</version>
-		</dependency>
 
 		<!-- 阿里云短信 -->
 		<dependency>
@@ -349,6 +346,20 @@
 				<artifactId>happy-common-tools</artifactId>
 				<version>${happyboot.version}</version>
 			</dependency>
+
+			<!-- redisson -->
+			<dependency>
+				<groupId>org.redisson</groupId>
+				<artifactId>redisson</artifactId>
+				<version>${redisson.version}</version>
+			</dependency>
+			<!-- guava工具类 -->
+			<dependency>
+				<groupId>com.google.guava</groupId>
+				<artifactId>guava</artifactId>
+				<version>${guava.version}</version>
+			</dependency>
+
 	    	<!-- 七牛云SDK -->
 			<dependency>
 				<groupId>com.qiniu</groupId>