diff --git a/opsli-base-support/opsli-common/src/main/java/org/opsli/common/constants/DictConstants.java b/opsli-base-support/opsli-common/src/main/java/org/opsli/common/constants/DictConstants.java index 619df17..f536f52 100644 --- a/opsli-base-support/opsli-common/src/main/java/org/opsli/common/constants/DictConstants.java +++ b/opsli-base-support/opsli-common/src/main/java/org/opsli/common/constants/DictConstants.java @@ -25,12 +25,12 @@ package org.opsli.common.constants; public interface DictConstants { /** 缓存前缀 NAME */ - String CACHE_PREFIX_NAME = "dict:name:"; + String CACHE_PREFIX_NAME = "dict::name::"; /** 缓存前缀 VALUE*/ - String CACHE_PREFIX_VALUE = "dict:value:"; + String CACHE_PREFIX_VALUE = "dict::value::"; /** 缓存前缀 LIST*/ - String CACHE_PREFIX_LIST = "dict:list:"; + String CACHE_PREFIX_LIST = "dict::list::"; } diff --git a/opsli-base-support/opsli-core/pom.xml b/opsli-base-support/opsli-core/pom.xml index 094a8b9..ce7a356 100644 --- a/opsli-base-support/opsli-core/pom.xml +++ b/opsli-base-support/opsli-core/pom.xml @@ -40,6 +40,13 @@ ${plugins.version} + + + org.opsliframework.boot + opsli-plugins-redisson + ${plugins.version} + + org.opsliframework.boot diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/autoconfigure/conf/RedissonConfig.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/autoconfigure/conf/RedissonConfig.java new file mode 100644 index 0000000..1da556c --- /dev/null +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/autoconfigure/conf/RedissonConfig.java @@ -0,0 +1,61 @@ +/** + * Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.opsli.core.autoconfigure.conf; + +import lombok.extern.slf4j.Slf4j; +import org.opsli.plugins.redisson.RedissonLock; +import org.opsli.plugins.redisson.RedissonManager; +import org.opsli.plugins.redisson.entity.RedissonProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; + +/** + * Redisson自动化配置 + * + * @author Parker + * @date 2019/6/19 下午11:55 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(name = "redisson.lock.server.enable", havingValue = "true") +@EnableConfigurationProperties(RedissonProperties.class) +public class RedissonConfig { + + @Bean + @ConditionalOnMissingBean + @Order(value = 2) + public RedissonLock redissonLock(RedissonManager redissonManager) { + RedissonLock redissonLock = new RedissonLock(redissonManager); + log.info("[RedissonLock]组装完毕"); + return redissonLock; + } + + @Bean + @ConditionalOnMissingBean + @Order(value = 1) + public RedissonManager redissonManager(RedissonProperties redissonProperties) { + RedissonManager redissonManager = + new RedissonManager(redissonProperties); + log.info("[RedissonManager]组装完毕,当前连接方式:" + redissonProperties.getType().getDesc() + + ",连接地址:" + redissonProperties.getAddress()); + return redissonManager; + } +} + diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/cache/local/CacheUtil.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/cache/local/CacheUtil.java index d7e17ed..ea2a91f 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/cache/local/CacheUtil.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/cache/local/CacheUtil.java @@ -649,7 +649,7 @@ public class CacheUtil { public void init(CacheProperties cacheProperties){ if(cacheProperties != null){ // 获得 超级管理员 - CacheUtil.PREFIX_NAME = Convert.toStr(cacheProperties.getPrefix(), "opsli") + ":"; + CacheUtil.PREFIX_NAME = Convert.toStr(cacheProperties.getPrefix(), "opsli") + "::"; } } diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/msg/CoreMsg.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/msg/CoreMsg.java index 2724f5a..eae5848 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/msg/CoreMsg.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/msg/CoreMsg.java @@ -38,6 +38,7 @@ public enum CoreMsg implements BaseMsg { * Redis */ REDIS_EXCEPTION_PUSH_SUB(10200,"Redis 订阅通道失败!"), + REDIS_EXCEPTION_LOCK(10201,"无法申领分布式锁"), /** * Excel diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/security/shiro/cache/RedisCacheManager.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/security/shiro/cache/RedisCacheManager.java index d77c149..f8850ec 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/security/shiro/cache/RedisCacheManager.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/security/shiro/cache/RedisCacheManager.java @@ -32,7 +32,7 @@ public class RedisCacheManager implements CacheManager { /** * The Redis key prefix for caches */ - public static final String DEFAULT_CACHE_KEY_PREFIX = "shiro:cache:"; + public static final String DEFAULT_CACHE_KEY_PREFIX = "shiro::cache::"; private String keyPrefix = DEFAULT_CACHE_KEY_PREFIX; public static final String DEFAULT_PRINCIPAL_ID_FIELD_NAME = "authCacheKey or id"; diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/CaptchaUtil.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/CaptchaUtil.java index 3c8ff26..926d37d 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/CaptchaUtil.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/CaptchaUtil.java @@ -58,7 +58,7 @@ public class CaptchaUtil { private static final List CAPTCHA_STRATEGY_LIST; /** 缓存前缀 */ - private static final String PREFIX = "temp:captcha:"; + private static final String PREFIX = "temp::captcha::"; /** 默认验证码保存 5 分钟 */ private static final int TIME_OUT = 300; /** Redis插件 */ diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/MenuUtil.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/MenuUtil.java index 53d9f5b..456e2f2 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/MenuUtil.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/MenuUtil.java @@ -46,7 +46,7 @@ import static org.opsli.common.constants.OrderConstants.UTIL_ORDER; public class MenuUtil { /** 前缀 */ - public static final String PREFIX_CODE = "menu:code:"; + public static final String PREFIX_CODE = "menu::code::"; /** Redis插件 */ diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/OrgUtil.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/OrgUtil.java index 3418416..e3143c7 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/OrgUtil.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/OrgUtil.java @@ -46,7 +46,7 @@ import static org.opsli.common.constants.OrderConstants.UTIL_ORDER; public class OrgUtil { /** 前缀 */ - public static final String PREFIX_CODE = "org:userId:"; + public static final String PREFIX_CODE = "org::userId::"; /** Redis插件 */ diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/SearchHisUtil.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/SearchHisUtil.java index 96d9014..029a0e2 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/SearchHisUtil.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/SearchHisUtil.java @@ -48,7 +48,7 @@ public class SearchHisUtil { /** 搜索历史缓存数据KEY */ private static final int DEFAULT_COUNT = 10; - private static final String CACHE_PREFIX = "his:username:"; + private static final String CACHE_PREFIX = "his::username::"; /** Redis插件 */ private static RedisPlugin redisPlugin; diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/TenantUtil.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/TenantUtil.java index 8ff8ee7..10d31ef 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/TenantUtil.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/TenantUtil.java @@ -46,7 +46,7 @@ import static org.opsli.common.constants.OrderConstants.UTIL_ORDER; public class TenantUtil { /** 前缀 */ - public static final String PREFIX_CODE = "tenant:id:"; + public static final String PREFIX_CODE = "tenant::id::"; /** Redis插件 */ diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserTokenUtil.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserTokenUtil.java index 657a517..e048bd6 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserTokenUtil.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserTokenUtil.java @@ -61,11 +61,11 @@ public class UserTokenUtil { /** token 缓存名 */ public static final String TOKEN_NAME = TokenConstants.ACCESS_TOKEN; /** 缓存前缀 */ - private static final String TICKET_PREFIX = "ticket:"; + private static final String TICKET_PREFIX = "ticket::"; /** 账号失败次数 */ - public static final String ACCOUNT_SLIP_COUNT_PREFIX = "account:slip:count:"; + public static final String ACCOUNT_SLIP_COUNT_PREFIX = "account::slip::count::"; /** 账号失败锁定KEY */ - public static final String ACCOUNT_SLIP_LOCK_PREFIX = "account:slip:lock:"; + public static final String ACCOUNT_SLIP_LOCK_PREFIX = "account::slip::lock::"; /** 限制登录数量 -1 为无限大 */ public static final int ACCOUNT_LIMIT_INFINITE = -1; /** 登录配置信息 */ diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserUtil.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserUtil.java index 9f4d69d..ee46173 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserUtil.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserUtil.java @@ -15,7 +15,9 @@ */ package org.opsli.core.utils; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; +import cn.hutool.core.thread.ThreadUtil; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.google.common.collect.Lists; @@ -31,16 +33,19 @@ import org.opsli.common.exception.TokenException; import org.opsli.core.autoconfigure.properties.GlobalProperties; import org.opsli.core.cache.local.CacheUtil; import org.opsli.core.cache.pushsub.msgs.UserMsgFactory; +import org.opsli.core.msg.CoreMsg; import org.opsli.core.msg.TokenMsg; import org.opsli.plugins.redis.RedisLockPlugins; import org.opsli.plugins.redis.RedisPlugin; import org.opsli.plugins.redis.lock.RedisLock; +import org.opsli.plugins.redisson.RedissonLock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.util.List; +import java.util.concurrent.TimeUnit; import static org.opsli.common.constants.OrderConstants.UTIL_ORDER; @@ -58,18 +63,18 @@ import static org.opsli.common.constants.OrderConstants.UTIL_ORDER; public class UserUtil { /** 前缀 */ - public static final String PREFIX_ID = "userId:"; - public static final String PREFIX_ID_ROLES = "userId:roles:"; - public static final String PREFIX_ID_PERMISSIONS = "userId:permissions:"; - public static final String PREFIX_ID_MENUS = "userId:menus:"; - public static final String PREFIX_USERNAME = "username:"; + public static final String PREFIX_ID = "userId::"; + public static final String PREFIX_ID_ROLES = "userId::roles::"; + public static final String PREFIX_ID_PERMISSIONS = "userId::permissions::"; + public static final String PREFIX_ID_MENUS = "userId::menus::"; + public static final String PREFIX_USERNAME = "username::"; /** Redis插件 */ private static RedisPlugin redisPlugin; - /** Redis分布式锁 */ - private static RedisLockPlugins redisLockPlugins; + /** Redisson 分布式锁 */ + private static RedissonLock REDISSON_LOCK; /** 用户Service */ private static UserApi userApi; @@ -104,36 +109,35 @@ public class UserUtil { * @return UserModel */ public static UserModel getUser(String userId){ + + // 缓存Key + String cacheKey = PREFIX_ID + userId; + // 先从缓存里拿 - UserModel userModel = CacheUtil.getTimed(UserModel.class, PREFIX_ID + userId); + UserModel userModel = CacheUtil.getTimed(UserModel.class, cacheKey); if (userModel != null){ return userModel; } - // 拿不到 -------- // 防止缓存穿透判断 - boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID + userId); + boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey); if(hasNilFlag){ return null; } - // 锁凭证 redisLock 贯穿全程 - RedisLock redisLock = new RedisLock(); - redisLock.setLockName(PREFIX_ID + userId) - .setAcquireTimeOut(3000L) - .setLockTimeOut(5000L); - try { - // 这里增加分布式锁 防止缓存击穿 - // ============ 尝试加锁 - redisLock = redisLockPlugins.tryLock(redisLock); - if(redisLock == null){ + // 分布式上锁 + boolean isLock = REDISSON_LOCK.tryLock( + cacheKey, 5); + if(!isLock){ + // 无法申领分布式锁 + log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage()); return null; } // 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求 - userModel = CacheUtil.getTimed(UserModel.class, PREFIX_ID + userId); + userModel = CacheUtil.getTimed(UserModel.class, cacheKey); if (userModel != null){ return userModel; } @@ -147,18 +151,22 @@ public class UserUtil { if(resultVo.isSuccess()){ userModel = resultVo.getData(); // 存入缓存 - CacheUtil.put(PREFIX_ID + userId, userModel); + CacheUtil.put(cacheKey, userModel); } + }catch (Exception e){ - log.error(e.getMessage(),e); + log.error(e.getMessage(), e); }finally { - // ============ 释放锁 - redisLockPlugins.unLock(redisLock); + // 是否是当前线程 + if(REDISSON_LOCK.isHeldByCurrentThread(cacheKey)){ + // 释放锁 + REDISSON_LOCK.unlock(cacheKey); + } } if(userModel == null){ // 设置空变量 用于防止穿透判断 - CacheUtil.putNilFlag(PREFIX_ID + userId); + CacheUtil.putNilFlag(cacheKey); return null; } @@ -172,56 +180,58 @@ public class UserUtil { * @return UserModel */ public static UserModel getUserByUserName(String userName){ + // 缓存Key + String cacheKey = PREFIX_USERNAME + userName; + // 先从缓存里拿 - UserModel userModel = CacheUtil.getTimed(UserModel.class, PREFIX_USERNAME + userName); + UserModel userModel = CacheUtil.getTimed(UserModel.class, cacheKey); if (userModel != null){ return userModel; } // 拿不到 -------- // 防止缓存穿透判断 - boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_USERNAME + userName); + boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey); if(hasNilFlag){ return null; } - // 锁凭证 redisLock 贯穿全程 - RedisLock redisLock = new RedisLock(); - redisLock.setLockName(PREFIX_USERNAME + userName) - .setAcquireTimeOut(3000L) - .setLockTimeOut(5000L); - try { - // 这里增加分布式锁 防止缓存击穿 - // ============ 尝试加锁 - redisLock = redisLockPlugins.tryLock(redisLock); - if(redisLock == null){ + // 分布式上锁 + boolean isLock = REDISSON_LOCK.tryLock( + cacheKey, 5); + if(!isLock){ + // 无法申领分布式锁 + log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage()); return null; } // 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求 - userModel = CacheUtil.getTimed(UserModel.class, PREFIX_USERNAME + userName); - if (userModel != null){ + userModel = CacheUtil.getTimed(UserModel.class, cacheKey); + if (userModel != null) { return userModel; } // 查询数据库 ResultVo resultVo = userApi.getUserByUsername(userName); - if(resultVo.isSuccess()){ + if (resultVo.isSuccess()) { userModel = resultVo.getData(); // 存入缓存 - CacheUtil.put(PREFIX_USERNAME + userName, userModel); + CacheUtil.put(cacheKey, userModel); } }catch (Exception e){ - log.error(e.getMessage(),e); + log.error(e.getMessage(), e); }finally { - // ============ 释放锁 - redisLockPlugins.unLock(redisLock); + // 是否是当前线程 + if(REDISSON_LOCK.isHeldByCurrentThread(cacheKey)){ + // 释放锁 + REDISSON_LOCK.unlock(cacheKey); + } } if(userModel == null){ // 设置空变量 用于防止穿透判断 - CacheUtil.putNilFlag(PREFIX_USERNAME + userName); + CacheUtil.putNilFlag(cacheKey); return null; } @@ -234,80 +244,62 @@ public class UserUtil { * @return List */ public static List getUserRolesByUserId(String userId){ - List roles = null; + // 缓存Key + String cacheKey = PREFIX_ID_ROLES + userId; + + List roles; // 先从缓存里拿 - try { - Object obj = CacheUtil.getTimed(PREFIX_ID_ROLES + userId); - if(obj instanceof List){ - List list = Convert.toList(String.class, obj); - if (!list.isEmpty()) { - return list; - } - }else { - JSONArray jsonArray = Convert.convert(JSONArray.class, obj); - if (jsonArray != null && !jsonArray.isEmpty()) { - return jsonArray.toJavaList(String.class); - } - } - }catch (Exception e){ - log.error(e.getMessage(), e); + Object obj = CacheUtil.getTimed(cacheKey); + roles = Convert.toList(String.class, obj); + if(CollUtil.isNotEmpty(roles)){ + return roles; } // 拿不到 -------- // 防止缓存穿透判断 - boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_ROLES + userId); + boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey); if(hasNilFlag){ return null; } - // 锁凭证 redisLock 贯穿全程 - RedisLock redisLock = new RedisLock(); - redisLock.setLockName(PREFIX_ID_ROLES + userId) - .setAcquireTimeOut(3000L) - .setLockTimeOut(5000L); - try { - // 这里增加分布式锁 防止缓存击穿 - // ============ 尝试加锁 - redisLock = redisLockPlugins.tryLock(redisLock); - if(redisLock == null){ + // 分布式上锁 + boolean isLock = REDISSON_LOCK.tryLock( + cacheKey, 5); + if(!isLock){ + // 无法申领分布式锁 + log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage()); return null; } // 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求 - try { - Object obj = CacheUtil.getTimed(PREFIX_ID_ROLES + userId); - if(obj instanceof List){ - List list = Convert.toList(String.class, obj); - if (!list.isEmpty()) { - return list; - } - }else { - JSONArray jsonArray = Convert.convert(JSONArray.class, obj); - if (jsonArray != null && !jsonArray.isEmpty()) { - return jsonArray.toJavaList(String.class); - } - } - }catch (Exception ignored){} + obj = CacheUtil.getTimed(cacheKey); + roles = Convert.toList(String.class, obj); + if(CollUtil.isNotEmpty(roles)){ + return roles; + } // 查询数据库 ResultVo> resultVo = userApi.getRolesByUserId(userId); if(resultVo.isSuccess()){ roles = resultVo.getData(); // 存入缓存 - CacheUtil.put(PREFIX_ID_ROLES + userId, roles); + CacheUtil.put(cacheKey, roles); } }catch (Exception e){ - log.error(e.getMessage(),e); + log.error(e.getMessage(), e); }finally { - // ============ 释放锁 - redisLockPlugins.unLock(redisLock); + // 是否是当前线程 + if(REDISSON_LOCK.isHeldByCurrentThread(cacheKey)){ + // 释放锁 + REDISSON_LOCK.unlock(cacheKey); + } } - if(roles == null || roles.size() == 0){ + if(CollUtil.isEmpty(roles)){ // 设置空变量 用于防止穿透判断 - CacheUtil.putNilFlag(PREFIX_ID_ROLES + userId); + CacheUtil.putNilFlag(cacheKey); return null; } @@ -321,81 +313,63 @@ public class UserUtil { * @return List */ public static List getUserAllPermsByUserId(String userId){ - List permissions = null; + + // 缓存Key + String cacheKey = PREFIX_ID_PERMISSIONS + userId; + + List permissions; // 先从缓存里拿 - try { - Object obj = CacheUtil.getTimed(PREFIX_ID_PERMISSIONS + userId); - if(obj instanceof List){ - List list = Convert.toList(String.class, obj); - if (!list.isEmpty()) { - return list; - } - }else { - JSONArray jsonArray = Convert.convert(JSONArray.class, obj); - if (jsonArray != null && !jsonArray.isEmpty()) { - return jsonArray.toJavaList(String.class); - } - } - }catch (Exception e){ - log.error(e.getMessage(), e); + Object obj = CacheUtil.getTimed(cacheKey); + permissions = Convert.toList(String.class, obj); + if(CollUtil.isNotEmpty(permissions)){ + return permissions; } - // 拿不到 -------- // 防止缓存穿透判断 - boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_PERMISSIONS + userId); + boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey); if(hasNilFlag){ return null; } - // 锁凭证 redisLock 贯穿全程 - RedisLock redisLock = new RedisLock(); - redisLock.setLockName(PREFIX_ID_PERMISSIONS + userId) - .setAcquireTimeOut(3000L) - .setLockTimeOut(5000L); - try { - // 这里增加分布式锁 防止缓存击穿 - // ============ 尝试加锁 - redisLock = redisLockPlugins.tryLock(redisLock); - if(redisLock == null){ + // 分布式上锁 + boolean isLock = REDISSON_LOCK.tryLock( + cacheKey, 5); + if(!isLock){ + // 无法申领分布式锁 + log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage()); return null; } // 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求 - try { - Object obj = CacheUtil.getTimed(PREFIX_ID_PERMISSIONS + userId); - if(obj instanceof List){ - List list = Convert.toList(String.class, obj); - if (!list.isEmpty()) { - return list; - } - }else { - JSONArray jsonArray = Convert.convert(JSONArray.class, obj); - if (jsonArray != null && !jsonArray.isEmpty()) { - return jsonArray.toJavaList(String.class); - } - } - }catch (Exception ignored){} + obj = CacheUtil.getTimed(cacheKey); + permissions = Convert.toList(String.class, obj); + if(CollUtil.isNotEmpty(permissions)){ + return permissions; + } // 查询数据库 ResultVo> resultVo = userApi.getAllPerms(userId); if(resultVo.isSuccess()){ permissions = resultVo.getData(); // 存入缓存 - CacheUtil.put(PREFIX_ID_PERMISSIONS + userId, permissions); + CacheUtil.put(cacheKey, permissions); } }catch (Exception e){ - log.error(e.getMessage(),e); + log.error(e.getMessage(), e); }finally { - // ============ 释放锁 - redisLockPlugins.unLock(redisLock); + // 是否是当前线程 + if(REDISSON_LOCK.isHeldByCurrentThread(cacheKey)){ + // 释放锁 + REDISSON_LOCK.unlock(cacheKey); + } } - if(permissions == null || permissions.size() == 0){ + if(CollUtil.isEmpty(permissions)){ // 设置空变量 用于防止穿透判断 - CacheUtil.putNilFlag(PREFIX_ID_PERMISSIONS + userId); + CacheUtil.putNilFlag(cacheKey); return null; } @@ -408,101 +382,64 @@ public class UserUtil { * @return List */ public static List getMenuListByUserId(String userId){ - List menus = null; + + // 缓存Key + String cacheKey = PREFIX_ID_MENUS + userId; + + List menus; // 先从缓存里拿 - try { - Object obj = CacheUtil.getTimed(PREFIX_ID_MENUS + userId); - if(obj instanceof List){ - List list = Convert.toList(obj); - if (!list.isEmpty()) { - List menuModels = Lists.newArrayListWithCapacity(list.size()); - for (Object menuObj : list) { - if(menuObj instanceof MenuModel){ - menuModels.add((MenuModel) menuObj); - }else if(menuObj instanceof JSONObject){ - JSONObject jsonObject = (JSONObject) menuObj; - MenuModel t = JSONObject.toJavaObject(jsonObject, MenuModel.class); - menuModels.add(t); - } - } - return menuModels; - } - }else { - JSONArray jsonArray = Convert.convert(JSONArray.class, obj); - if (jsonArray != null && !jsonArray.isEmpty()) { - return jsonArray.toJavaList(MenuModel.class); - } - } - }catch (Exception e){ - log.error(e.getMessage(), e); + Object obj = CacheUtil.getTimed(cacheKey); + menus = Convert.toList(MenuModel.class, obj); + if(CollUtil.isNotEmpty(menus)){ + return menus; } - // 拿不到 -------- // 防止缓存穿透判断 - boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_MENUS + userId); + boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey); if(hasNilFlag){ return null; } - // 锁凭证 redisLock 贯穿全程 - RedisLock redisLock = new RedisLock(); - redisLock.setLockName(PREFIX_ID_MENUS + userId) - .setAcquireTimeOut(3000L) - .setLockTimeOut(5000L); try { - // 这里增加分布式锁 防止缓存击穿 - // ============ 尝试加锁 - redisLock = redisLockPlugins.tryLock(redisLock); - if(redisLock == null){ + // 分布式上锁 + boolean isLock = REDISSON_LOCK.tryLock( + cacheKey, 5); + if(!isLock){ + // 无法申领分布式锁 + log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage()); return null; } // 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求 - try { - Object obj = CacheUtil.getTimed(PREFIX_ID_MENUS + userId); - if(obj instanceof List){ - List list = Convert.toList(obj); - if (!list.isEmpty()) { - List menuModels = Lists.newArrayListWithCapacity(list.size()); - for (Object menuObj : list) { - if(menuObj instanceof MenuModel){ - menuModels.add((MenuModel) menuObj); - }else if(menuObj instanceof JSONObject){ - JSONObject jsonObject = (JSONObject) menuObj; - MenuModel t = JSONObject.toJavaObject(jsonObject, MenuModel.class); - menuModels.add(t); - } - } - return menuModels; - } - }else { - JSONArray jsonArray = Convert.convert(JSONArray.class, obj); - if (jsonArray != null && !jsonArray.isEmpty()) { - return jsonArray.toJavaList(MenuModel.class); - } - } - }catch (Exception ignored){} + obj = CacheUtil.getTimed(cacheKey); + menus = Convert.toList(MenuModel.class, obj); + if(CollUtil.isNotEmpty(menus)){ + return menus; + } // 查询数据库 ResultVo> resultVo = userApi.getMenuListByUserId(userId); if(resultVo.isSuccess()){ menus = resultVo.getData(); // 存入缓存 - CacheUtil.put(PREFIX_ID_MENUS + userId, menus); + CacheUtil.put(cacheKey, menus); } }catch (Exception e){ - log.error(e.getMessage(),e); + log.error(e.getMessage(), e); }finally { - // ============ 释放锁 - redisLockPlugins.unLock(redisLock); + // 是否是当前线程 + if(REDISSON_LOCK.isHeldByCurrentThread(cacheKey)){ + // 释放锁 + REDISSON_LOCK.unlock(cacheKey); + } } - if(menus == null || menus.size() == 0){ + if(CollUtil.isEmpty(menus)){ // 设置空变量 用于防止穿透判断 - CacheUtil.putNilFlag(PREFIX_ID_MENUS + userId); + CacheUtil.putNilFlag(cacheKey); return null; } @@ -727,28 +664,25 @@ public class UserUtil { UserUtil.redisPlugin = redisPlugin; } - @Autowired - public void setRedisLockPlugins(RedisLockPlugins redisLockPlugins) { - UserUtil.redisLockPlugins = redisLockPlugins; - } - @Autowired public void setUserApi(UserApi userApi) { UserUtil.userApi = userApi; } + /** * 初始化 * @param globalProperties 配置类 */ @Autowired - public void init(GlobalProperties globalProperties){ + public void init(GlobalProperties globalProperties, RedissonLock redissonLock){ if(globalProperties != null && globalProperties.getAuth() != null && globalProperties.getAuth().getToken() != null ){ // 获得 超级管理员 UserUtil.SUPER_ADMIN = globalProperties.getAuth().getSuperAdmin(); } + UserUtil.REDISSON_LOCK = redissonLock; } } diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/lock/RedisLock.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/lock/RedisLock.java index ab13800..d90c337 100644 --- a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/lock/RedisLock.java +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/lock/RedisLock.java @@ -27,7 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class RedisLock { - private static final String LOCK_PREFIX = "lock:"; + private static final String LOCK_PREFIX = "lock::"; /** 锁名称 */ private String lockName; diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/lock/RedisLockImpl.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/lock/RedisLockImpl.java index ba36276..0d2bfa8 100644 --- a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/lock/RedisLockImpl.java +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/lock/RedisLockImpl.java @@ -53,7 +53,7 @@ import java.util.List; public class RedisLockImpl implements RedisLockPlugins { /** 锁前缀 */ - private static final String LOCK_NAME_PREFIX = "lock:"; + private static final String LOCK_NAME_PREFIX = "lock::"; @Autowired private RedisPlugin redisPlugin; diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/receiver/BaseReceiver.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/receiver/BaseReceiver.java index 0a46a1d..62985e2 100644 --- a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/receiver/BaseReceiver.java +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/receiver/BaseReceiver.java @@ -11,7 +11,7 @@ package org.opsli.plugins.redis.pushsub.receiver; */ public abstract class BaseReceiver { - public static final String BASE_CHANNEL = "listener:msg:"; + public static final String BASE_CHANNEL = "listener::msg::"; private final String channel; public BaseReceiver(String channel){ diff --git a/opsli-plugins/opsli-plugins-redisson/README.md b/opsli-plugins/opsli-plugins-redisson/README.md new file mode 100644 index 0000000..07451e4 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/README.md @@ -0,0 +1,35 @@ +## application.properties配置方式(yaml同理) +### redisson分布式锁配置--单机 + + redisson.lock.server.address=127.0.0.1:6379 + redisson.lock.server.type=standalone + redisson.lock.server.password= + redisson.lock.server.database=1 + +### redisson分布式锁配置--哨兵 +**redisson.lock.server.address** 格式为: sentinel.conf配置里的sentinel别名,sentinel1节点的服务IP和端口,sentinel2节点的服务IP和端口,sentinel3节点的服务IP和端口 +
比如sentinel.conf里配置为sentinel monitor my-sentinel-name 127.0.0.1 6379 2,那么这里就配置my-sentinel-name + + redisson.server.address=my-sentinel-name,127.0.0.1:26379,127.0.0.1:26389,127.0.0.1:26399 + redisson.server.type=sentinel + redisson.lock.server.password= + redisson.lock.server.database=1 + +### redisson分布式锁配置--集群方式 +cluster方式至少6个节点(3主3从,3主做sharding,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 + + redisson.server.address=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 + redisson.server.type=cluster + redisson.lock.server.password= + redisson.lock.server.database=1 + +### redisson分布式锁配置--主从 +地址格式为**主节点,子节点,子节点** +
比如:127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381 +
代表主节点:127.0.0.1:6379,从节点127.0.0.1:6380,127.0.0.1:6381 + + redisson.server.address=127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381 + redisson.server.type=masterslave + redisson.lock.server.password= + redisson.lock.server.database=1 diff --git a/opsli-plugins/opsli-plugins-redisson/pom.xml b/opsli-plugins/opsli-plugins-redisson/pom.xml new file mode 100644 index 0000000..375d15c --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/pom.xml @@ -0,0 +1,25 @@ + + + + opsli-plugins + org.opsliframework.boot + 1.0.0 + ../pom.xml + + + 4.0.0 + opsli-plugins-redisson + ${project.parent.version} + + + + + org.redisson + redisson + + + + + \ No newline at end of file diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/RedissonLock.java b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/RedissonLock.java new file mode 100644 index 0000000..35cad7c --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/RedissonLock.java @@ -0,0 +1,122 @@ +package org.opsli.plugins.redisson; + +import lombok.extern.slf4j.Slf4j; +import org.redisson.Redisson; +import org.redisson.api.RLock; + +import java.util.concurrent.TimeUnit; + +/** + * @Description: 针对源码Redisson进行一层封装 + * + * @author xub + * @date 2019/6/19 下午10:26 + */ +@Slf4j +public class RedissonLock { + + private static final String PREFIX = "lock::"; + + private RedissonManager redissonManager; + private Redisson redisson; + + + public RedissonLock(RedissonManager redissonManager) { + this.redissonManager = redissonManager; + this.redisson = redissonManager.getRedisson(); + } + + public RedissonLock() {} + + /** + * 加锁操作 (设置锁的有效时间) + * @param lockName 锁名称 + * @param leaseTime 锁有效时间 + */ + public void lock(String lockName, long leaseTime) { + RLock rLock = redisson.getLock(PREFIX + lockName); + rLock.lock(leaseTime,TimeUnit.SECONDS); + } + + /** + * 加锁操作 (锁有效时间采用默认时间30秒) + * @param lockName 锁名称 + */ + public void lock(String lockName) { + RLock rLock = redisson.getLock(PREFIX + lockName); + rLock.lock(); + } + + /** + * 加锁操作(tryLock锁,没有等待时间) + * @param lockName 锁名称 + * @param leaseTime 锁有效时间 + */ + public boolean tryLock(String lockName, long leaseTime) { + + RLock rLock = redisson.getLock(PREFIX + lockName); + boolean getLock; + try { + getLock = rLock.tryLock( leaseTime, TimeUnit.SECONDS); + } catch (InterruptedException e) { + log.error("获取Redisson分布式锁[异常],lockName=" + lockName, e); + e.printStackTrace(); + return false; + } + return getLock; + } + + /** + * 加锁操作(tryLock锁,有等待时间) + * @param lockName 锁名称 + * @param leaseTime 锁有效时间 + * @param waitTime 等待时间 + */ + public boolean tryLock(String lockName, long leaseTime, long waitTime) { + RLock rLock = redisson.getLock(PREFIX + lockName); + boolean getLock; + try { + getLock = rLock.tryLock( waitTime, leaseTime, TimeUnit.SECONDS); + } catch (InterruptedException e) { + log.error("获取Redisson分布式锁[异常],lockName=" + lockName, e); + e.printStackTrace(); + return false; + } + return getLock; + } + + /** + * 解锁 + * @param lockName 锁名称 + */ + public void unlock(String lockName) { + redisson.getLock(PREFIX + lockName).unlock(); + } + + /** + * 判断该锁是否已经被线程持有 + * @param lockName 锁名称 + */ + public boolean isLock(String lockName) { + RLock rLock = redisson.getLock(PREFIX + lockName); + return rLock.isLocked(); + } + + + /** + * 判断该线程是否持有当前锁 + * @param lockName 锁名称 + */ + public boolean isHeldByCurrentThread(String lockName) { + RLock rLock = redisson.getLock(PREFIX + lockName); + return rLock.isHeldByCurrentThread(); + } + + public RedissonManager getRedissonManager() { + return redissonManager; + } + + public void setRedissonManager(RedissonManager redissonManager) { + this.redissonManager = redissonManager; + } +} diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/RedissonManager.java b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/RedissonManager.java new file mode 100644 index 0000000..fb8d698 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/RedissonManager.java @@ -0,0 +1,113 @@ +package org.opsli.plugins.redisson; + +import cn.hutool.core.util.ClassUtil; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.opsli.plugins.redisson.entity.RedissonProperties; +import org.opsli.plugins.redisson.enums.RedissonType; +import org.opsli.plugins.redisson.strategy.RedissonConfigService; +import org.redisson.Redisson; +import org.redisson.config.Config; + +import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.Set; + + +/** + * @Description: Redisson核心配置,用于提供初始化的redisson实例 + * + * @author xub + * @date 2019/6/19 下午10:16 + */ +@Slf4j +public class RedissonManager { + + private final Redisson redisson; + + public RedissonManager(RedissonProperties redissonProperties) { + try { + //通过不同部署方式获得不同config实体 + Config config = RedissonConfigFactory.getInstance().createConfig(redissonProperties); + redisson = (Redisson) Redisson.create(config); + } catch (Exception e) { + log.error("Redisson 初始化异常", e); + throw new IllegalArgumentException("请输入正确的配置," + + "connectionType必须在 standalone/sentinel/cluster/masterslave"); + } + } + + public Redisson getRedisson() { + return redisson; + } + + /** + * Redisson连接方式配置工厂 + * 双重检查锁 + */ + static class RedissonConfigFactory { + + /** 策略集合 */ + private final Map strategyMap = Maps.newHashMap(); + + private RedissonConfigFactory() { + Set> clazzSet = ClassUtil.scanPackageBySuper( + RedissonConfigService.class.getPackage().getName()+".impl", + RedissonConfigService.class + ); + for (Class aClass : clazzSet) { + // 位运算 去除抽象类 + if((aClass.getModifiers() & Modifier.ABSTRACT) != 0){ + continue; + } + + try { + Object obj = aClass.newInstance(); + RedissonConfigService handler = (RedissonConfigService) obj; + // 加入集合 + strategyMap.put(handler.getType(),handler); + } catch (Exception e){ + log.error(e.getMessage(), e); + } + } + } + + 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 redis连接信息 + * @return Config + */ + Config createConfig(RedissonProperties redissonProperties) { + Preconditions.checkNotNull(redissonProperties); + Preconditions.checkNotNull(redissonProperties.getAddress(), "redisson.lock.server.address 不能为空!"); + Preconditions.checkNotNull(redissonProperties.getType().getType(), "redisson.lock.server.password 不能为空!"); + Preconditions.checkNotNull(redissonProperties.getDatabase(), "redisson.lock.server.database cannot 不能为空"); + String connectionType = redissonProperties.getType().getType(); + //声明配置上下文 + RedissonConfigService redissonConfigService = strategyMap.get(redissonProperties.getType()); + if (redissonConfigService == null){ + throw new IllegalArgumentException("创建Redisson连接Config失败!当前连接方式:" + connectionType); + } + return redissonConfigService.createRedissonConfig(redissonProperties); + } + } + +} + + diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/annotation/DistributedLock.java b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/annotation/DistributedLock.java new file mode 100644 index 0000000..e0a5a21 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/annotation/DistributedLock.java @@ -0,0 +1,28 @@ +package org.opsli.plugins.redisson.annotation; + +import java.lang.annotation.*; + +/** + * @Description: 基于注解的分布式式锁 + * + * @author xub + * @date 2019/6/19 下午9:22 + */ +@Documented +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface DistributedLock { + + /** + * 锁的名称 + */ + String value() default "redisson"; + + /** + * 锁的有效时间 + */ + int leaseTime() default 10; +} + + diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/annotation/DistributedLockHandler.java b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/annotation/DistributedLockHandler.java new file mode 100644 index 0000000..641268d --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/annotation/DistributedLockHandler.java @@ -0,0 +1,55 @@ +package org.opsli.plugins.redisson.annotation; + + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; + + +import org.opsli.plugins.redisson.RedissonLock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + + +/** + * @Description: Redisson分布式锁注解解析器 + * + * @author xub + * @date 2019/6/20 下午9:34 + */ +@Aspect +@Component +@Slf4j +public class DistributedLockHandler { + + @Autowired + private RedissonLock redissonLock; + + @Around("@annotation(distributedLock)") + public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) { + Object returnValue = null; + + log.info("[开始]执行RedisLock环绕通知,获取Redis分布式锁开始"); + //获取锁名称 + String lockName = distributedLock.value(); + //获取超时时间,默认10秒 + int leaseTime = distributedLock.leaseTime(); + redissonLock.lock(lockName, leaseTime); + try { + log.info("获取Redis分布式锁[成功],加锁完成,开始执行业务逻辑..."); + returnValue = joinPoint.proceed(); + } catch (Throwable throwable) { + log.error("获取Redis分布式锁[异常],加锁失败", throwable); + throwable.printStackTrace(); + } finally { + //如果该线程还持有该锁,那么释放该锁。如果该线程不持有该锁,说明该线程的锁已到过期时间,自动释放锁 + if (redissonLock.isHeldByCurrentThread(lockName)) { + redissonLock.unlock(lockName); + } + } + log.info("释放Redis分布式锁[成功],解锁完成,结束业务逻辑..."); + + return returnValue; + } +} diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/constant/GlobalConstant.java b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/constant/GlobalConstant.java new file mode 100644 index 0000000..99a4b73 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/constant/GlobalConstant.java @@ -0,0 +1,29 @@ +package org.opsli.plugins.redisson.constant; + + +/** + * @author xub + * @Description: 全局常量枚举 用来拼接完整的URL + * @date 2019/6/19 下午9:09 + */ +public enum GlobalConstant { + + /** 前缀 */ + REDIS_CONNECTION_PREFIX("redis://", "Redis地址配置前缀"); + + private final String constant_value; + private final String constant_desc; + + GlobalConstant(String constant_value, String constant_desc) { + this.constant_value = constant_value; + this.constant_desc = constant_desc; + } + + public String getConstant_value() { + return constant_value; + } + + public String getConstant_desc() { + return constant_desc; + } +} diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/entity/RedissonProperties.java b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/entity/RedissonProperties.java new file mode 100644 index 0000000..f68a7bf --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/entity/RedissonProperties.java @@ -0,0 +1,56 @@ +package org.opsli.plugins.redisson.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.opsli.plugins.redisson.enums.RedissonType; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @Description: 读取redis配置信息,封装到当前实体中 + * + * @author xub + * @date 2019/6/19 下午9:35 + */ +@ConfigurationProperties(prefix = "redisson.lock.server") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RedissonProperties { + + /** + * 是否启用 + */ + private boolean enable; + + /** + * redis主机地址,ip:port,有多个用半角逗号分隔 + */ + private String address; + + /** + * 连接类型,支持standalone-单机节点,sentinel-哨兵,cluster-集群,masterslave-主从 + */ + private RedissonType type = RedissonType.STANDALONE; + + /** + * redis 连接密码 + */ + private String password; + + /** + * 选取那个数据库 + */ + private int database; + + public RedissonProperties setPassword(String password) { + this.password = password; + return this; + } + + public RedissonProperties setDatabase(int database) { + this.database = database; + return this; + } + +} diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/enums/RedissonType.java b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/enums/RedissonType.java new file mode 100644 index 0000000..92cd610 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/enums/RedissonType.java @@ -0,0 +1,68 @@ +/** + * Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.opsli.plugins.redisson.enums; + + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.common.enums + * @Author: Parker + * @CreateTime: 2020-09-17 23:40 + * @Description: Redisson 类型 + */ +public enum RedissonType { + + /** 类型 */ + STANDALONE("standalone", "单节点部署方式"), + SENTINEL("sentinel", "哨兵部署方式"), + CLUSTER("cluster", "集群方式"), + MASTER_SLAVE("master_slave", "主从部署方式"); + + ; + + + private final String type; + private final String desc; + + public static RedissonType getType(String type) { + RedissonType[] var1 = values(); + int var2 = var1.length; + + for (RedissonType e : var1) { + if (e.type.equalsIgnoreCase(type)) { + return e; + } + } + + return null; + } + + public String getType() { + return this.type; + } + + public String getDesc() { + return this.desc; + } + + // ================ + + RedissonType(final String type, final String desc) { + this.type = type; + this.desc = desc; + } + +} diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/RedissonConfigService.java b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/RedissonConfigService.java new file mode 100644 index 0000000..9827d9a --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/RedissonConfigService.java @@ -0,0 +1,28 @@ +package org.opsli.plugins.redisson.strategy; + + +import org.opsli.plugins.redisson.entity.RedissonProperties; +import org.opsli.plugins.redisson.enums.RedissonType; +import org.redisson.config.Config; + +/** + * @Description: Redisson配置构建接口 + * + * @author xub + * @date 2019/6/20 下午3:35 + */ +public interface RedissonConfigService { + + /** + * 获得类型 + * @return type + */ + RedissonType getType(); + + /** + * 根据不同的Redis配置策略创建对应的Config + * @param redissonProperties + * @return Config + */ + Config createRedissonConfig(RedissonProperties redissonProperties); +} diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/impl/ClusterConfigImpl.java b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/impl/ClusterConfigImpl.java new file mode 100644 index 0000000..de3bf7a --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/impl/ClusterConfigImpl.java @@ -0,0 +1,51 @@ +package org.opsli.plugins.redisson.strategy.impl; + + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.opsli.plugins.redisson.constant.GlobalConstant; +import org.opsli.plugins.redisson.entity.RedissonProperties; +import org.opsli.plugins.redisson.enums.RedissonType; +import org.opsli.plugins.redisson.strategy.RedissonConfigService; +import org.redisson.config.Config; + +/** + * @Description: 集群方式Redisson部署 + * 地址格式: + * cluster方式至少6个节点(3主3从,3主做sharding,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 xub + * @date 2019/6/19 下午4:24 + */ +@Slf4j +public class ClusterConfigImpl implements RedissonConfigService { + + @Override + public RedissonType getType() { + return RedissonType.CLUSTER; + } + + @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 (String addrToken : addrTokens) { + config.useClusterServers() + .addNodeAddress(GlobalConstant.REDIS_CONNECTION_PREFIX.getConstant_value() + addrToken); + if (StringUtils.isNotBlank(password)) { + config.useClusterServers().setPassword(password); + } + } + log.info("初始化[集群部署]方式Config,redisAddress:" + address); + } catch (Exception e) { + log.error("集群部署 Redisson init error", e); + e.printStackTrace(); + } + return config; + } +} diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/impl/MasterslaveConfigImpl.java b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/impl/MasterslaveConfigImpl.java new file mode 100644 index 0000000..1f3b2e6 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/impl/MasterslaveConfigImpl.java @@ -0,0 +1,65 @@ +package org.opsli.plugins.redisson.strategy.impl; + + +import cn.hutool.core.convert.Convert; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.opsli.plugins.redisson.constant.GlobalConstant; +import org.opsli.plugins.redisson.entity.RedissonProperties; +import org.opsli.plugins.redisson.enums.RedissonType; +import org.opsli.plugins.redisson.strategy.RedissonConfigService; +import org.redisson.config.Config; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Description: 主从部署Redisson配置 + * 连接方式: 主节点,子节点,子节点 + * 格式为: 127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381 + * @author xub + * @date 2019/6/19 下午9:21 + */ + +@Slf4j +public class MasterslaveConfigImpl implements RedissonConfigService { + + @Override + public RedissonType getType() { + return RedissonType.MASTER_SLAVE; + } + + @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 slaveList = Lists.newArrayList(); + for (String addrToken : addrTokens) { + slaveList.add(GlobalConstant.REDIS_CONNECTION_PREFIX.getConstant_value() + addrToken); + } + slaveList.remove(0); + + config.useMasterSlaveServers().addSlaveAddress( + Convert.toStrArray(slaveList)); + log.info("初始化[主从部署]方式Config,redisAddress:" + address); + } catch (Exception e) { + log.error("主从部署 Redisson init error", e); + e.printStackTrace(); + } + return config; + } + +} diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/impl/SentinelConfigImpl.java b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/impl/SentinelConfigImpl.java new file mode 100644 index 0000000..b2ba29f --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/impl/SentinelConfigImpl.java @@ -0,0 +1,53 @@ +package org.opsli.plugins.redisson.strategy.impl; + + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.opsli.plugins.redisson.constant.GlobalConstant; +import org.opsli.plugins.redisson.entity.RedissonProperties; +import org.opsli.plugins.redisson.enums.RedissonType; +import org.opsli.plugins.redisson.strategy.RedissonConfigService; +import org.redisson.config.Config; + + +/** + * @Description: 哨兵集群部署Redis连接配置 + * + * @author xub + * @date 2019/6/19 下午9:17 + */ +@Slf4j +public class SentinelConfigImpl implements RedissonConfigService { + + @Override + public RedissonType getType() { + return RedissonType.SENTINEL; + } + + @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); + } + //设置sentinel节点的服务IP和端口 + for (int i = 1; i < addrTokens.length; i++) { + config.useSentinelServers().addSentinelAddress(GlobalConstant.REDIS_CONNECTION_PREFIX.getConstant_value() + addrTokens[i]); + } + log.info("初始化[哨兵部署]方式Config,redisAddress:" + address); + } catch (Exception e) { + log.error("哨兵部署 Redisson init error", e); + + } + return config; + } +} diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/impl/StandaloneConfigImpl.java b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/impl/StandaloneConfigImpl.java new file mode 100644 index 0000000..70b648d --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/java/org/opsli/plugins/redisson/strategy/impl/StandaloneConfigImpl.java @@ -0,0 +1,47 @@ +package org.opsli.plugins.redisson.strategy.impl; + + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.opsli.plugins.redisson.constant.GlobalConstant; +import org.opsli.plugins.redisson.entity.RedissonProperties; +import org.opsli.plugins.redisson.enums.RedissonType; +import org.opsli.plugins.redisson.strategy.RedissonConfigService; +import org.redisson.config.Config; + +/** + * @Description: 单机部署Redisson配置 + * + * @author xub + * @date 2019/6/19 下午10:04 + */ +@Slf4j +public class StandaloneConfigImpl implements RedissonConfigService { + + @Override + public RedissonType getType() { + return RedissonType.STANDALONE; + } + + + @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.getConstant_value() + address; + config.useSingleServer().setAddress(redisAddr); + config.useSingleServer().setDatabase(database); + //密码可以为空 + if (StringUtils.isNotBlank(password)) { + config.useSingleServer().setPassword(password); + } + log.info("初始化[单机部署]方式Config,redisAddress:" + address); + } catch (Exception e) { + log.error("单机部署 Redisson init error", e); + } + return config; + } +} diff --git a/opsli-plugins/opsli-plugins-redisson/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/opsli-plugins/opsli-plugins-redisson/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000..180c941 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redisson/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,36 @@ +{ + "properties": [ + { + "name": "redisson.lock.server.enable", + "sourceType": "org.opsli.plugins.redisson.entity.RedissonProperties", + "type": "java.lang.Boolean", + "defaultValue": false, + "description": "redissonLock 是否启用." + }, + { + "name": "redisson.lock.server.address", + "sourceType": "org.opsli.plugins.redisson.entity.RedissonProperties", + "type": "java.lang.String", + "defaultValue": "127.0.0.1:6379", + "description": "redis主机地址,ip:port,有多个用半角逗号分隔." + }, + { + "name": "redisson.lock.server.type", + "sourceType": "org.opsli.plugins.redisson.entity.RedissonProperties", + "description": "连接类型,支持standalone-单机节点,sentinel-哨兵,cluster-集群,masterslave-主从." + }, + { + "name": "redisson.lock.server.password", + "sourceType": "org.opsli.plugins.redisson.entity.RedissonProperties", + "type": "java.lang.String", + "description": "redis 连接密码." + }, + { + "name": "redisson.lock.server.database", + "sourceType": "org.opsli.plugins.redisson.entity.RedissonProperties", + "type": "java.lang.Integer", + "defaultValue": 0, + "description": "选取那个数据库 ." + } + ] +} diff --git a/opsli-plugins/pom.xml b/opsli-plugins/pom.xml index 57afe84..57f2a6a 100644 --- a/opsli-plugins/pom.xml +++ b/opsli-plugins/pom.xml @@ -20,6 +20,7 @@ opsli-plugins-redis opsli-plugins-ehcache opsli-plugins-excel + opsli-plugins-redisson diff --git a/opsli-starter/src/main/resources/application-beta.yaml b/opsli-starter/src/main/resources/application-beta.yaml index 1fce5ee..88aa0fa 100644 --- a/opsli-starter/src/main/resources/application-beta.yaml +++ b/opsli-starter/src/main/resources/application-beta.yaml @@ -44,6 +44,15 @@ spring: #password: 12345678 #driver-class-name: com.mysql.cj.jdbc.Driver +# Redisson 分布式锁 +redisson: + lock: + server: + enable: true + type: standalone + address: "127.0.0.1:6379" + password: 123456 + database: 0 # knife4j 文档 配置 knife4j: @@ -59,7 +68,6 @@ knife4j: production: false - # opsli 自定义配置 opsli: # 演示模式 @@ -71,3 +79,4 @@ opsli: # 系统日志 log: path: /var/log + diff --git a/opsli-starter/src/main/resources/application-release.yaml b/opsli-starter/src/main/resources/application-release.yaml index 7c67bfa..1c79f77 100644 --- a/opsli-starter/src/main/resources/application-release.yaml +++ b/opsli-starter/src/main/resources/application-release.yaml @@ -44,6 +44,15 @@ spring: #password: 12345678 #driver-class-name: com.mysql.cj.jdbc.Driver +# Redisson 分布式锁 +redisson: + lock: + server: + enable: true + type: standalone + address: "127.0.0.1:6379" + password: 123456 + database: 0 # knife4j 文档 配置 knife4j: diff --git a/pom.xml b/pom.xml index 0ad7dcc..159fdb4 100644 --- a/pom.xml +++ b/pom.xml @@ -116,6 +116,13 @@ ${jwt.version} + + + org.redisson + redisson + 3.15.0 + +