common-redis
This commit is contained in:
parent
e04a663fdb
commit
c80d8909dd
@ -14,6 +14,7 @@
|
||||
<module>wol-common-core</module>
|
||||
<module>wol-common-doc</module>
|
||||
<module>wol-common-mybatis</module>
|
||||
<module>wol-common-redis</module>
|
||||
</modules>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
|
||||
@ -32,6 +32,11 @@
|
||||
<artifactId>wol-common-mybatis</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>wol-common-redis</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
@ -1,86 +1,85 @@
|
||||
package com.agileboot.common.core.constant;
|
||||
|
||||
|
||||
/**
|
||||
* 通用常量信息
|
||||
*
|
||||
* @author valarchie
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class Constants {
|
||||
private Constants() {
|
||||
}
|
||||
public interface Constants {
|
||||
|
||||
public static final int KB = 1024;
|
||||
|
||||
public static final int MB = KB * 1024;
|
||||
|
||||
public static final int GB = MB * 1024;
|
||||
|
||||
/**
|
||||
* http请求
|
||||
*/
|
||||
public static final String HTTP = "http://";
|
||||
|
||||
/**
|
||||
* https请求
|
||||
*/
|
||||
public static final String HTTPS = "https://";
|
||||
|
||||
|
||||
public static class Token {
|
||||
|
||||
private Token() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 令牌前缀
|
||||
*/
|
||||
public static final String PREFIX = "Bearer ";
|
||||
|
||||
/**
|
||||
* 令牌前缀
|
||||
*/
|
||||
public static final String LOGIN_USER_KEY = "login_user_key";
|
||||
|
||||
}
|
||||
|
||||
public static class Captcha {
|
||||
|
||||
private Captcha() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 令牌
|
||||
*/
|
||||
public static final String MATH_TYPE = "math";
|
||||
|
||||
/**
|
||||
* 令牌前缀
|
||||
*/
|
||||
public static final String CHAR_TYPE = "char";
|
||||
|
||||
}
|
||||
int KB = 1024;
|
||||
int MB = KB * 1024;
|
||||
int GB = MB * 1024;
|
||||
|
||||
/**
|
||||
* 资源映射路径 前缀
|
||||
*/
|
||||
public static final String RESOURCE_PREFIX = "profile";
|
||||
String RESOURCE_PREFIX = "profile";
|
||||
|
||||
public static class UploadSubDir {
|
||||
/**
|
||||
* UTF-8 字符集
|
||||
*/
|
||||
String UTF8 = "UTF-8";
|
||||
|
||||
private UploadSubDir() {
|
||||
}
|
||||
/**
|
||||
* GBK 字符集
|
||||
*/
|
||||
String GBK = "GBK";
|
||||
|
||||
public static final String IMPORT_PATH = "import";
|
||||
/**
|
||||
* www主域
|
||||
*/
|
||||
String WWW = "www.";
|
||||
|
||||
public static final String AVATAR_PATH = "avatar";
|
||||
/**
|
||||
* http请求
|
||||
*/
|
||||
String HTTP = "http://";
|
||||
|
||||
public static final String DOWNLOAD_PATH = "download";
|
||||
/**
|
||||
* https请求
|
||||
*/
|
||||
String HTTPS = "https://";
|
||||
|
||||
public static final String UPLOAD_PATH = "upload";
|
||||
/**
|
||||
* 通用成功标识
|
||||
*/
|
||||
String SUCCESS = "0";
|
||||
|
||||
}
|
||||
/**
|
||||
* 通用失败标识
|
||||
*/
|
||||
String FAIL = "1";
|
||||
|
||||
/**
|
||||
* 登录成功
|
||||
*/
|
||||
String LOGIN_SUCCESS = "Success";
|
||||
|
||||
/**
|
||||
* 注销
|
||||
*/
|
||||
String LOGOUT = "Logout";
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
String REGISTER = "Register";
|
||||
|
||||
/**
|
||||
* 登录失败
|
||||
*/
|
||||
String LOGIN_FAIL = "Error";
|
||||
|
||||
/**
|
||||
* 验证码有效期(分钟)
|
||||
*/
|
||||
Integer CAPTCHA_EXPIRATION = 2;
|
||||
|
||||
/**
|
||||
* 顶级部门id
|
||||
*/
|
||||
Long TOP_PARENT_ID = 0L;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,93 @@
|
||||
package com.agileboot.common.core.constant;
|
||||
|
||||
/**
|
||||
* 返回状态码
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface HttpStatus {
|
||||
/**
|
||||
* 操作成功
|
||||
*/
|
||||
int SUCCESS = 200;
|
||||
|
||||
/**
|
||||
* 对象创建成功
|
||||
*/
|
||||
int CREATED = 201;
|
||||
|
||||
/**
|
||||
* 请求已经被接受
|
||||
*/
|
||||
int ACCEPTED = 202;
|
||||
|
||||
/**
|
||||
* 操作已经执行成功,但是没有返回数据
|
||||
*/
|
||||
int NO_CONTENT = 204;
|
||||
|
||||
/**
|
||||
* 资源已被移除
|
||||
*/
|
||||
int MOVED_PERM = 301;
|
||||
|
||||
/**
|
||||
* 重定向
|
||||
*/
|
||||
int SEE_OTHER = 303;
|
||||
|
||||
/**
|
||||
* 资源没有被修改
|
||||
*/
|
||||
int NOT_MODIFIED = 304;
|
||||
|
||||
/**
|
||||
* 参数列表错误(缺少,格式不匹配)
|
||||
*/
|
||||
int BAD_REQUEST = 400;
|
||||
|
||||
/**
|
||||
* 未授权
|
||||
*/
|
||||
int UNAUTHORIZED = 401;
|
||||
|
||||
/**
|
||||
* 访问受限,授权过期
|
||||
*/
|
||||
int FORBIDDEN = 403;
|
||||
|
||||
/**
|
||||
* 资源,服务未找到
|
||||
*/
|
||||
int NOT_FOUND = 404;
|
||||
|
||||
/**
|
||||
* 不允许的http方法
|
||||
*/
|
||||
int BAD_METHOD = 405;
|
||||
|
||||
/**
|
||||
* 资源冲突,或者资源被锁
|
||||
*/
|
||||
int CONFLICT = 409;
|
||||
|
||||
/**
|
||||
* 不支持的数据,媒体类型
|
||||
*/
|
||||
int UNSUPPORTED_TYPE = 415;
|
||||
|
||||
/**
|
||||
* 系统内部错误
|
||||
*/
|
||||
int ERROR = 500;
|
||||
|
||||
/**
|
||||
* 接口未实现
|
||||
*/
|
||||
int NOT_IMPLEMENTED = 501;
|
||||
|
||||
/**
|
||||
* 系统警告消息
|
||||
*/
|
||||
int WARN = 601;
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
package com.agileboot.common.core.core;
|
||||
|
||||
import com.agileboot.common.core.constant.HttpStatus;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 响应信息主体
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class R<T> implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
public static final int SUCCESS = 200;
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
public static final int FAIL = 500;
|
||||
|
||||
/**
|
||||
* 消息状态码
|
||||
*/
|
||||
private int code;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 数据对象
|
||||
*/
|
||||
private T data;
|
||||
|
||||
public static <T> R<T> ok() {
|
||||
return restResult(null, SUCCESS, "操作成功");
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(T data) {
|
||||
return restResult(data, SUCCESS, "操作成功");
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(String msg) {
|
||||
return restResult(null, SUCCESS, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(String msg, T data) {
|
||||
return restResult(data, SUCCESS, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail() {
|
||||
return restResult(null, FAIL, "操作失败");
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(String msg) {
|
||||
return restResult(null, FAIL, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(T data) {
|
||||
return restResult(data, FAIL, "操作失败");
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(String msg, T data) {
|
||||
return restResult(data, FAIL, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(int code, String msg) {
|
||||
return restResult(null, code, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回警告消息
|
||||
*
|
||||
* @param msg 返回内容
|
||||
* @return 警告消息
|
||||
*/
|
||||
public static <T> R<T> warn(String msg) {
|
||||
return restResult(null, HttpStatus.WARN, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回警告消息
|
||||
*
|
||||
* @param msg 返回内容
|
||||
* @param data 数据对象
|
||||
* @return 警告消息
|
||||
*/
|
||||
public static <T> R<T> warn(String msg, T data) {
|
||||
return restResult(data, HttpStatus.WARN, msg);
|
||||
}
|
||||
|
||||
private static <T> R<T> restResult(T data, int code, String msg) {
|
||||
R<T> r = new R<>();
|
||||
r.setCode(code);
|
||||
r.setData(data);
|
||||
r.setMsg(msg);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static <T> Boolean isError(R<T> ret) {
|
||||
return !isSuccess(ret);
|
||||
}
|
||||
|
||||
public static <T> Boolean isSuccess(R<T> ret) {
|
||||
return R.SUCCESS == ret.getCode();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
package com.agileboot.common.core.core.dto;
|
||||
|
||||
import com.agileboot.common.core.exception.ApiException;
|
||||
import com.agileboot.common.core.exception.error.ErrorCode;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 响应信息主体
|
||||
*
|
||||
* @author valarchie
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class ResponseDTO<T> {
|
||||
|
||||
private Integer code;
|
||||
|
||||
private String msg;
|
||||
|
||||
@JsonInclude
|
||||
private T data;
|
||||
|
||||
public static <T> ResponseDTO<T> ok() {
|
||||
return build(null, ErrorCode.SUCCESS.code(), ErrorCode.SUCCESS.message());
|
||||
}
|
||||
|
||||
public static <T> ResponseDTO<T> ok(T data) {
|
||||
return build(data, ErrorCode.SUCCESS.code(), ErrorCode.SUCCESS.message());
|
||||
}
|
||||
|
||||
public static <T> ResponseDTO<T> fail() {
|
||||
return build(null, ErrorCode.FAILED.code(), ErrorCode.FAILED.message());
|
||||
}
|
||||
|
||||
public static <T> ResponseDTO<T> fail(T data) {
|
||||
return build(data, ErrorCode.FAILED.code(), ErrorCode.FAILED.message());
|
||||
}
|
||||
|
||||
public static <T> ResponseDTO<T> fail(ApiException exception) {
|
||||
return build(null, exception.getErrorCode().code(), exception.getMessage());
|
||||
}
|
||||
|
||||
public static <T> ResponseDTO<T> fail(ApiException exception, T data) {
|
||||
return build(data, exception.getErrorCode().code(), exception.getMessage());
|
||||
}
|
||||
|
||||
public static <T> ResponseDTO<T> build(T data, Integer code, String msg) {
|
||||
return new ResponseDTO<>(code, msg, data);
|
||||
}
|
||||
|
||||
// 去掉直接填充错误码的方式, 这种方式不能拿到i18n的错误消息 统一通过ApiException来构造错误消息
|
||||
// public static <T> ResponseDTO<T> fail(ErrorCodeInterface code, Object... args) {
|
||||
// return build(null, code, args);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
42
agileboot-common/wol-common-redis/pom.xml
Normal file
42
agileboot-common/wol-common-redis/pom.xml
Normal file
@ -0,0 +1,42 @@
|
||||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>agileboot-common</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>wol-common-redis</artifactId>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.agileboot</groupId>
|
||||
<artifactId>wol-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,45 @@
|
||||
package com.agileboot.common.redis.config;
|
||||
|
||||
import com.agileboot.common.redis.manager.PlusSpringCacheManager;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 缓存配置
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableCaching
|
||||
public class CacheConfiguration {
|
||||
|
||||
/**
|
||||
* caffeine 本地缓存处理器
|
||||
*/
|
||||
@Bean
|
||||
public Cache<Object, Object> caffeine() {
|
||||
return Caffeine.newBuilder()
|
||||
// 设置最后一次写入或访问后经过固定时间过期
|
||||
.expireAfterWrite(30, TimeUnit.SECONDS)
|
||||
// 初始的缓存空间大小
|
||||
.initialCapacity(100)
|
||||
// 缓存的最大条数
|
||||
.maximumSize(1000)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义缓存管理器 整合spring-cache
|
||||
*/
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
return new PlusSpringCacheManager();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,161 @@
|
||||
package com.agileboot.common.redis.config;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.agileboot.common.redis.config.properties.RedissonProperties;
|
||||
import com.agileboot.common.redis.handler.KeyPrefixHandler;
|
||||
import com.agileboot.common.redis.handler.RedisExceptionHandler;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.redisson.client.codec.StringCodec;
|
||||
import org.redisson.codec.CompositeCodec;
|
||||
import org.redisson.codec.TypedJsonJacksonCodec;
|
||||
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.thread.Threading;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.task.VirtualThreadTaskExecutor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* redis配置
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(RedissonProperties.class)
|
||||
public class RedisConfiguration {
|
||||
|
||||
@Autowired
|
||||
private RedissonProperties redissonProperties;
|
||||
|
||||
@Bean
|
||||
public RedissonAutoConfigurationCustomizer redissonCustomizer() {
|
||||
return config -> {
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
om.registerModule(javaTimeModule);
|
||||
om.setTimeZone(TimeZone.getDefault());
|
||||
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
// 指定序列化输入的类型,类必须是非final修饰的。序列化时将对象全类名一起保存下来
|
||||
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
|
||||
// LoggerFactory.useSlf4jLogging(true);
|
||||
// FuryCodec furyCodec = new FuryCodec();
|
||||
// CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, furyCodec, furyCodec);
|
||||
TypedJsonJacksonCodec jsonCodec = new TypedJsonJacksonCodec(Object.class, om);
|
||||
// 组合序列化 key 使用 String 内容使用通用 json 格式
|
||||
CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, jsonCodec, jsonCodec);
|
||||
config.setThreads(redissonProperties.getThreads())
|
||||
.setNettyThreads(redissonProperties.getNettyThreads())
|
||||
// 缓存 Lua 脚本 减少网络传输(redisson 大部分的功能都是基于 Lua 脚本实现)
|
||||
.setUseScriptCache(true)
|
||||
.setCodec(codec);
|
||||
if (Threading.VIRTUAL.isActive(SpringUtil.getBean(Environment.class))) {
|
||||
config.setNettyExecutor(new VirtualThreadTaskExecutor("redisson-"));
|
||||
}
|
||||
RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
|
||||
if (ObjectUtil.isNotNull(singleServerConfig)) {
|
||||
// 使用单机模式
|
||||
config.useSingleServer()
|
||||
//设置redis key前缀
|
||||
.setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))
|
||||
.setTimeout(singleServerConfig.getTimeout())
|
||||
.setClientName(singleServerConfig.getClientName())
|
||||
.setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
|
||||
.setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
|
||||
.setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
|
||||
.setConnectionPoolSize(singleServerConfig.getConnectionPoolSize());
|
||||
}
|
||||
// 集群配置方式 参考下方注释
|
||||
RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
|
||||
if (ObjectUtil.isNotNull(clusterServersConfig)) {
|
||||
config.useClusterServers()
|
||||
//设置redis key前缀
|
||||
.setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))
|
||||
.setTimeout(clusterServersConfig.getTimeout())
|
||||
.setClientName(clusterServersConfig.getClientName())
|
||||
.setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())
|
||||
.setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())
|
||||
.setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())
|
||||
.setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())
|
||||
.setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())
|
||||
.setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())
|
||||
.setReadMode(clusterServersConfig.getReadMode())
|
||||
.setSubscriptionMode(clusterServersConfig.getSubscriptionMode());
|
||||
}
|
||||
log.info("初始化 redis 配置");
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常处理器
|
||||
*/
|
||||
@Bean
|
||||
public RedisExceptionHandler redisExceptionHandler() {
|
||||
return new RedisExceptionHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* redis集群配置 yml
|
||||
*
|
||||
* --- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉)
|
||||
* spring.data:
|
||||
* redis:
|
||||
* cluster:
|
||||
* nodes:
|
||||
* - 192.168.0.100:6379
|
||||
* - 192.168.0.101:6379
|
||||
* - 192.168.0.102:6379
|
||||
* # 密码
|
||||
* password:
|
||||
* # 连接超时时间
|
||||
* timeout: 10s
|
||||
* # 是否开启ssl
|
||||
* ssl.enabled: false
|
||||
*
|
||||
* redisson:
|
||||
* # 线程池数量
|
||||
* threads: 16
|
||||
* # Netty线程池数量
|
||||
* nettyThreads: 32
|
||||
* # 集群配置
|
||||
* clusterServersConfig:
|
||||
* # 客户端名称
|
||||
* clientName: ${ruoyi.name}
|
||||
* # master最小空闲连接数
|
||||
* masterConnectionMinimumIdleSize: 32
|
||||
* # master连接池大小
|
||||
* masterConnectionPoolSize: 64
|
||||
* # slave最小空闲连接数
|
||||
* slaveConnectionMinimumIdleSize: 32
|
||||
* # slave连接池大小
|
||||
* slaveConnectionPoolSize: 64
|
||||
* # 连接空闲超时,单位:毫秒
|
||||
* idleConnectionTimeout: 10000
|
||||
* # 命令等待超时,单位:毫秒
|
||||
* timeout: 3000
|
||||
* # 发布和订阅连接池大小
|
||||
* subscriptionConnectionPoolSize: 50
|
||||
* # 读取模式
|
||||
* readMode: "SLAVE"
|
||||
* # 订阅模式
|
||||
* subscriptionMode: "MASTER"
|
||||
*/
|
||||
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
package com.agileboot.common.redis.config.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.redisson.config.ReadMode;
|
||||
import org.redisson.config.SubscriptionMode;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Redisson 配置属性
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "redisson")
|
||||
public class RedissonProperties {
|
||||
|
||||
/**
|
||||
* redis缓存key前缀
|
||||
*/
|
||||
private String keyPrefix;
|
||||
|
||||
/**
|
||||
* 线程池数量,默认值 = 当前处理核数量 * 2
|
||||
*/
|
||||
private int threads;
|
||||
|
||||
/**
|
||||
* Netty线程池数量,默认值 = 当前处理核数量 * 2
|
||||
*/
|
||||
private int nettyThreads;
|
||||
|
||||
/**
|
||||
* 单机服务配置
|
||||
*/
|
||||
private SingleServerConfig singleServerConfig;
|
||||
|
||||
/**
|
||||
* 集群服务配置
|
||||
*/
|
||||
private ClusterServersConfig clusterServersConfig;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class SingleServerConfig {
|
||||
|
||||
/**
|
||||
* 客户端名称
|
||||
*/
|
||||
private String clientName;
|
||||
|
||||
/**
|
||||
* 最小空闲连接数
|
||||
*/
|
||||
private int connectionMinimumIdleSize;
|
||||
|
||||
/**
|
||||
* 连接池大小
|
||||
*/
|
||||
private int connectionPoolSize;
|
||||
|
||||
/**
|
||||
* 连接空闲超时,单位:毫秒
|
||||
*/
|
||||
private int idleConnectionTimeout;
|
||||
|
||||
/**
|
||||
* 命令等待超时,单位:毫秒
|
||||
*/
|
||||
private int timeout;
|
||||
|
||||
/**
|
||||
* 发布和订阅连接池大小
|
||||
*/
|
||||
private int subscriptionConnectionPoolSize;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public static class ClusterServersConfig {
|
||||
|
||||
/**
|
||||
* 客户端名称
|
||||
*/
|
||||
private String clientName;
|
||||
|
||||
/**
|
||||
* master最小空闲连接数
|
||||
*/
|
||||
private int masterConnectionMinimumIdleSize;
|
||||
|
||||
/**
|
||||
* master连接池大小
|
||||
*/
|
||||
private int masterConnectionPoolSize;
|
||||
|
||||
/**
|
||||
* slave最小空闲连接数
|
||||
*/
|
||||
private int slaveConnectionMinimumIdleSize;
|
||||
|
||||
/**
|
||||
* slave连接池大小
|
||||
*/
|
||||
private int slaveConnectionPoolSize;
|
||||
|
||||
/**
|
||||
* 连接空闲超时,单位:毫秒
|
||||
*/
|
||||
private int idleConnectionTimeout;
|
||||
|
||||
/**
|
||||
* 命令等待超时,单位:毫秒
|
||||
*/
|
||||
private int timeout;
|
||||
|
||||
/**
|
||||
* 发布和订阅连接池大小
|
||||
*/
|
||||
private int subscriptionConnectionPoolSize;
|
||||
|
||||
/**
|
||||
* 读取模式
|
||||
*/
|
||||
private ReadMode readMode;
|
||||
|
||||
/**
|
||||
* 订阅模式
|
||||
*/
|
||||
private SubscriptionMode subscriptionMode;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package com.agileboot.common.redis.handler;
|
||||
|
||||
import io.micrometer.common.util.StringUtils;
|
||||
import org.redisson.api.NameMapper;
|
||||
|
||||
/**
|
||||
* redis缓存key前缀处理
|
||||
*
|
||||
* @author ye
|
||||
* @date 2022/7/14 17:44
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public class KeyPrefixHandler implements NameMapper {
|
||||
|
||||
private final String keyPrefix;
|
||||
|
||||
public KeyPrefixHandler(String keyPrefix) {
|
||||
//前缀为空 则返回空前缀
|
||||
this.keyPrefix = StringUtils.isBlank(keyPrefix) ? "" : keyPrefix + ":";
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加前缀
|
||||
*/
|
||||
@Override
|
||||
public String map(String name) {
|
||||
if (StringUtils.isBlank(name)) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isNotBlank(keyPrefix) && !name.startsWith(keyPrefix)) {
|
||||
return keyPrefix + name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除前缀
|
||||
*/
|
||||
@Override
|
||||
public String unmap(String name) {
|
||||
if (StringUtils.isBlank(name)) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isNotBlank(keyPrefix) && name.startsWith(keyPrefix)) {
|
||||
return name.substring(keyPrefix.length());
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package com.agileboot.common.redis.handler;
|
||||
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.agileboot.common.core.core.R;
|
||||
import com.baomidou.lock.exception.LockFailureException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* Redis异常处理器
|
||||
*
|
||||
* @author AprilWind
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class RedisExceptionHandler {
|
||||
|
||||
/**
|
||||
* 分布式锁Lock4j异常
|
||||
*/
|
||||
@ExceptionHandler(LockFailureException.class)
|
||||
public R<Void> handleLockFailureException(LockFailureException e, HttpServletRequest request) {
|
||||
String requestURI = request.getRequestURI();
|
||||
log.error("获取锁失败了'{}',发生Lock4j异常.", requestURI, e);
|
||||
return R.fail(HttpStatus.HTTP_UNAVAILABLE, "业务处理中,请稍后再试...");
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
package com.agileboot.common.redis.manager;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import org.springframework.cache.Cache;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* Cache 装饰器模式(用于扩展 Caffeine 一级缓存)
|
||||
*
|
||||
* @author LionLi
|
||||
*/
|
||||
public class CaffeineCacheDecorator implements Cache {
|
||||
|
||||
private static final com.github.benmanes.caffeine.cache.Cache<Object, Object>
|
||||
CAFFEINE = SpringUtil.getBean("caffeine");
|
||||
|
||||
private final String name;
|
||||
private final Cache cache;
|
||||
|
||||
public CaffeineCacheDecorator(String name, Cache cache) {
|
||||
this.name = name;
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getNativeCache() {
|
||||
return cache.getNativeCache();
|
||||
}
|
||||
|
||||
public String getUniqueKey(Object key) {
|
||||
return name + ":" + key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueWrapper get(Object key) {
|
||||
Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key));
|
||||
return (ValueWrapper) o;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T get(Object key, Class<T> type) {
|
||||
Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, type));
|
||||
return (T) o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Object key, Object value) {
|
||||
CAFFEINE.invalidate(getUniqueKey(key));
|
||||
cache.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueWrapper putIfAbsent(Object key, Object value) {
|
||||
CAFFEINE.invalidate(getUniqueKey(key));
|
||||
return cache.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evict(Object key) {
|
||||
evictIfPresent(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evictIfPresent(Object key) {
|
||||
boolean b = cache.evictIfPresent(key);
|
||||
if (b) {
|
||||
CAFFEINE.invalidate(getUniqueKey(key));
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
CAFFEINE.invalidateAll();
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean invalidate() {
|
||||
return cache.invalidate();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T get(Object key, Callable<T> valueLoader) {
|
||||
Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, valueLoader));
|
||||
return (T) o;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,202 @@
|
||||
/**
|
||||
* Copyright (c) 2013-2021 Nikita Koksharov
|
||||
*
|
||||
* 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 com.agileboot.common.redis.manager;
|
||||
|
||||
import com.agileboot.common.redis.utils.RedisUtils;
|
||||
import org.redisson.api.RMap;
|
||||
import org.redisson.api.RMapCache;
|
||||
import org.redisson.spring.cache.CacheConfig;
|
||||
import org.redisson.spring.cache.RedissonCache;
|
||||
import org.springframework.boot.convert.DurationStyle;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* A {@link CacheManager} implementation
|
||||
* backed by Redisson instance.
|
||||
* <p>
|
||||
* 修改 RedissonSpringCacheManager 源码
|
||||
* 重写 cacheName 处理方法 支持多参数
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class PlusSpringCacheManager implements CacheManager {
|
||||
|
||||
private boolean dynamic = true;
|
||||
|
||||
private boolean allowNullValues = true;
|
||||
|
||||
private boolean transactionAware = true;
|
||||
|
||||
Map<String, CacheConfig> configMap = new ConcurrentHashMap<>();
|
||||
ConcurrentMap<String, Cache> instanceMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Creates CacheManager supplied by Redisson instance
|
||||
*/
|
||||
public PlusSpringCacheManager() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines possibility of storing {@code null} values.
|
||||
* <p>
|
||||
* Default is <code>true</code>
|
||||
*
|
||||
* @param allowNullValues stores if <code>true</code>
|
||||
*/
|
||||
public void setAllowNullValues(boolean allowNullValues) {
|
||||
this.allowNullValues = allowNullValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if cache aware of Spring-managed transactions.
|
||||
* If {@code true} put/evict operations are executed only for successful transaction in after-commit phase.
|
||||
* <p>
|
||||
* Default is <code>false</code>
|
||||
*
|
||||
* @param transactionAware cache is transaction aware if <code>true</code>
|
||||
*/
|
||||
public void setTransactionAware(boolean transactionAware) {
|
||||
this.transactionAware = transactionAware;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines 'fixed' cache names.
|
||||
* A new cache instance will not be created in dynamic for non-defined names.
|
||||
* <p>
|
||||
* `null` parameter setups dynamic mode
|
||||
*
|
||||
* @param names of caches
|
||||
*/
|
||||
public void setCacheNames(Collection<String> names) {
|
||||
if (names != null) {
|
||||
for (String name : names) {
|
||||
getCache(name);
|
||||
}
|
||||
dynamic = false;
|
||||
} else {
|
||||
dynamic = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cache config mapped by cache name
|
||||
*
|
||||
* @param config object
|
||||
*/
|
||||
public void setConfig(Map<String, ? extends CacheConfig> config) {
|
||||
this.configMap = (Map<String, CacheConfig>) config;
|
||||
}
|
||||
|
||||
protected CacheConfig createDefaultConfig() {
|
||||
return new CacheConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cache getCache(String name) {
|
||||
// 重写 cacheName 支持多参数
|
||||
String[] array = StringUtils.delimitedListToStringArray(name, "#");
|
||||
name = array[0];
|
||||
|
||||
Cache cache = instanceMap.get(name);
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
}
|
||||
if (!dynamic) {
|
||||
return cache;
|
||||
}
|
||||
|
||||
CacheConfig config = configMap.get(name);
|
||||
if (config == null) {
|
||||
config = createDefaultConfig();
|
||||
configMap.put(name, config);
|
||||
}
|
||||
|
||||
if (array.length > 1) {
|
||||
config.setTTL(DurationStyle.detectAndParse(array[1]).toMillis());
|
||||
}
|
||||
if (array.length > 2) {
|
||||
config.setMaxIdleTime(DurationStyle.detectAndParse(array[2]).toMillis());
|
||||
}
|
||||
if (array.length > 3) {
|
||||
config.setMaxSize(Integer.parseInt(array[3]));
|
||||
}
|
||||
int local = 1;
|
||||
if (array.length > 4) {
|
||||
local = Integer.parseInt(array[4]);
|
||||
}
|
||||
|
||||
if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) {
|
||||
return createMap(name, config, local);
|
||||
}
|
||||
|
||||
return createMapCache(name, config, local);
|
||||
}
|
||||
|
||||
private Cache createMap(String name, CacheConfig config, int local) {
|
||||
RMap<Object, Object> map = RedisUtils.getClient().getMap(name);
|
||||
|
||||
Cache cache = new RedissonCache(map, allowNullValues);
|
||||
if (local == 1) {
|
||||
cache = new CaffeineCacheDecorator(name, cache);
|
||||
}
|
||||
if (transactionAware) {
|
||||
cache = new TransactionAwareCacheDecorator(cache);
|
||||
}
|
||||
Cache oldCache = instanceMap.putIfAbsent(name, cache);
|
||||
if (oldCache != null) {
|
||||
cache = oldCache;
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private Cache createMapCache(String name, CacheConfig config, int local) {
|
||||
RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);
|
||||
|
||||
Cache cache = new RedissonCache(map, config, allowNullValues);
|
||||
if (local == 1) {
|
||||
cache = new CaffeineCacheDecorator(name, cache);
|
||||
}
|
||||
if (transactionAware) {
|
||||
cache = new TransactionAwareCacheDecorator(cache);
|
||||
}
|
||||
Cache oldCache = instanceMap.putIfAbsent(name, cache);
|
||||
if (oldCache != null) {
|
||||
cache = oldCache;
|
||||
} else {
|
||||
map.setMaxSize(config.getMaxSize());
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getCacheNames() {
|
||||
return Collections.unmodifiableSet(configMap.keySet());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package com.agileboot.common.redis.utils;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
|
||||
/**
|
||||
* 缓存操作工具类
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@SuppressWarnings(value = {"unchecked"})
|
||||
public class CacheUtils {
|
||||
|
||||
private static final CacheManager CACHE_MANAGER = SpringUtil.getBean(CacheManager.class);
|
||||
|
||||
/**
|
||||
* 获取缓存值
|
||||
*
|
||||
* @param cacheNames 缓存组名称
|
||||
* @param key 缓存key
|
||||
*/
|
||||
public static <T> T get(String cacheNames, Object key) {
|
||||
Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key);
|
||||
return wrapper != null ? (T) wrapper.get() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存缓存值
|
||||
*
|
||||
* @param cacheNames 缓存组名称
|
||||
* @param key 缓存key
|
||||
* @param value 缓存值
|
||||
*/
|
||||
public static void put(String cacheNames, Object key, Object value) {
|
||||
CACHE_MANAGER.getCache(cacheNames).put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存值
|
||||
*
|
||||
* @param cacheNames 缓存组名称
|
||||
* @param key 缓存key
|
||||
*/
|
||||
public static void evict(String cacheNames, Object key) {
|
||||
CACHE_MANAGER.getCache(cacheNames).evict(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空缓存值
|
||||
*
|
||||
* @param cacheNames 缓存组名称
|
||||
*/
|
||||
public static void clear(String cacheNames) {
|
||||
CACHE_MANAGER.getCache(cacheNames).clear();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,581 @@
|
||||
package com.agileboot.common.redis.utils;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.redisson.api.*;
|
||||
import org.redisson.api.options.KeysScanOptions;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* redis 工具类
|
||||
*
|
||||
* @author Lion Li
|
||||
* @version 3.1.0 新增
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@SuppressWarnings(value = {"unchecked", "rawtypes"})
|
||||
public class RedisUtils {
|
||||
|
||||
private static final RedissonClient CLIENT = SpringUtil.getBean(RedissonClient.class);
|
||||
|
||||
/**
|
||||
* 限流
|
||||
*
|
||||
* @param key 限流key
|
||||
* @param rateType 限流类型
|
||||
* @param rate 速率
|
||||
* @param rateInterval 速率间隔
|
||||
* @return -1 表示失败
|
||||
*/
|
||||
public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) {
|
||||
return rateLimiter(key, rateType, rate, rateInterval, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 限流
|
||||
*
|
||||
* @param key 限流key
|
||||
* @param rateType 限流类型
|
||||
* @param rate 速率
|
||||
* @param rateInterval 速率间隔
|
||||
* @param timeout 超时时间
|
||||
* @return -1 表示失败
|
||||
*/
|
||||
public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval, int timeout) {
|
||||
RRateLimiter rateLimiter = CLIENT.getRateLimiter(key);
|
||||
rateLimiter.trySetRate(rateType, rate, Duration.ofSeconds(rateInterval), Duration.ofSeconds(timeout));
|
||||
if (rateLimiter.tryAcquire()) {
|
||||
return rateLimiter.availablePermits();
|
||||
} else {
|
||||
return -1L;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端实例
|
||||
*/
|
||||
public static RedissonClient getClient() {
|
||||
return CLIENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布通道消息
|
||||
*
|
||||
* @param channelKey 通道key
|
||||
* @param msg 发送数据
|
||||
* @param consumer 自定义处理
|
||||
*/
|
||||
public static <T> void publish(String channelKey, T msg, Consumer<T> consumer) {
|
||||
RTopic topic = CLIENT.getTopic(channelKey);
|
||||
topic.publish(msg);
|
||||
consumer.accept(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布消息到指定的频道
|
||||
*
|
||||
* @param channelKey 通道key
|
||||
* @param msg 发送数据
|
||||
*/
|
||||
public static <T> void publish(String channelKey, T msg) {
|
||||
RTopic topic = CLIENT.getTopic(channelKey);
|
||||
topic.publish(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅通道接收消息
|
||||
*
|
||||
* @param channelKey 通道key
|
||||
* @param clazz 消息类型
|
||||
* @param consumer 自定义处理
|
||||
*/
|
||||
public static <T> void subscribe(String channelKey, Class<T> clazz, Consumer<T> consumer) {
|
||||
RTopic topic = CLIENT.getTopic(channelKey);
|
||||
topic.addListener(clazz, (channel, msg) -> consumer.accept(msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存基本的对象,Integer、String、实体类等
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param value 缓存的值
|
||||
*/
|
||||
public static <T> void setCacheObject(final String key, final T value) {
|
||||
setCacheObject(key, value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存基本的对象,保留当前对象 TTL 有效期
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param value 缓存的值
|
||||
* @param isSaveTtl 是否保留TTL有效期(例如: set之前ttl剩余90 set之后还是为90)
|
||||
* @since Redis 6.X 以上使用 setAndKeepTTL 兼容 5.X 方案
|
||||
*/
|
||||
public static <T> void setCacheObject(final String key, final T value, final boolean isSaveTtl) {
|
||||
RBucket<T> bucket = CLIENT.getBucket(key);
|
||||
if (isSaveTtl) {
|
||||
try {
|
||||
bucket.setAndKeepTTL(value);
|
||||
} catch (Exception e) {
|
||||
long timeToLive = bucket.remainTimeToLive();
|
||||
if (timeToLive == -1) {
|
||||
bucket.set(value);
|
||||
} else {
|
||||
bucket.set(value, Duration.ofMillis(timeToLive));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bucket.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存基本的对象,Integer、String、实体类等
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param value 缓存的值
|
||||
* @param duration 时间
|
||||
*/
|
||||
public static <T> void setCacheObject(final String key, final T value, final Duration duration) {
|
||||
RBucket<T> bucket = CLIENT.getBucket(key);
|
||||
bucket.set(value, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果不存在则设置 并返回 true 如果存在则返回 false
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param value 缓存的值
|
||||
* @return set成功或失败
|
||||
*/
|
||||
public static <T> boolean setObjectIfAbsent(final String key, final T value, final Duration duration) {
|
||||
RBucket<T> bucket = CLIENT.getBucket(key);
|
||||
return bucket.setIfAbsent(value, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果存在则设置 并返回 true 如果存在则返回 false
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param value 缓存的值
|
||||
* @return set成功或失败
|
||||
*/
|
||||
public static <T> boolean setObjectIfExists(final String key, final T value, final Duration duration) {
|
||||
RBucket<T> bucket = CLIENT.getBucket(key);
|
||||
return bucket.setIfExists(value, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册对象监听器
|
||||
* <p>
|
||||
* key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param listener 监听器配置
|
||||
*/
|
||||
public static <T> void addObjectListener(final String key, final ObjectListener listener) {
|
||||
RBucket<T> result = CLIENT.getBucket(key);
|
||||
result.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置有效时间
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param timeout 超时时间
|
||||
* @return true=设置成功;false=设置失败
|
||||
*/
|
||||
public static boolean expire(final String key, final long timeout) {
|
||||
return expire(key, Duration.ofSeconds(timeout));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置有效时间
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param duration 超时时间
|
||||
* @return true=设置成功;false=设置失败
|
||||
*/
|
||||
public static boolean expire(final String key, final Duration duration) {
|
||||
RBucket rBucket = CLIENT.getBucket(key);
|
||||
return rBucket.expire(duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的基本对象。
|
||||
*
|
||||
* @param key 缓存键值
|
||||
* @return 缓存键值对应的数据
|
||||
*/
|
||||
public static <T> T getCacheObject(final String key) {
|
||||
RBucket<T> rBucket = CLIENT.getBucket(key);
|
||||
return rBucket.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得key剩余存活时间
|
||||
*
|
||||
* @param key 缓存键值
|
||||
* @return 剩余存活时间
|
||||
*/
|
||||
public static <T> long getTimeToLive(final String key) {
|
||||
RBucket<T> rBucket = CLIENT.getBucket(key);
|
||||
return rBucket.remainTimeToLive();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单个对象
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
*/
|
||||
public static boolean deleteObject(final String key) {
|
||||
return CLIENT.getBucket(key).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除集合对象
|
||||
*
|
||||
* @param collection 多个对象
|
||||
*/
|
||||
public static void deleteObject(final Collection collection) {
|
||||
RBatch batch = CLIENT.createBatch();
|
||||
collection.forEach(t -> {
|
||||
batch.getBucket(t.toString()).deleteAsync();
|
||||
});
|
||||
batch.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查缓存对象是否存在
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
*/
|
||||
public static boolean isExistsObject(final String key) {
|
||||
return CLIENT.getBucket(key).isExists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存List数据
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param dataList 待缓存的List数据
|
||||
* @return 缓存的对象
|
||||
*/
|
||||
public static <T> boolean setCacheList(final String key, final List<T> dataList) {
|
||||
RList<T> rList = CLIENT.getList(key);
|
||||
return rList.addAll(dataList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加缓存List数据
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param data 待缓存的数据
|
||||
* @return 缓存的对象
|
||||
*/
|
||||
public static <T> boolean addCacheList(final String key, final T data) {
|
||||
RList<T> rList = CLIENT.getList(key);
|
||||
return rList.add(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册List监听器
|
||||
* <p>
|
||||
* key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param listener 监听器配置
|
||||
*/
|
||||
public static <T> void addListListener(final String key, final ObjectListener listener) {
|
||||
RList<T> rList = CLIENT.getList(key);
|
||||
rList.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的list对象
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @return 缓存键值对应的数据
|
||||
*/
|
||||
public static <T> List<T> getCacheList(final String key) {
|
||||
RList<T> rList = CLIENT.getList(key);
|
||||
return rList.readAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的list对象(范围)
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param form 起始下标
|
||||
* @param to 截止下标
|
||||
* @return 缓存键值对应的数据
|
||||
*/
|
||||
public static <T> List<T> getCacheListRange(final String key, int form, int to) {
|
||||
RList<T> rList = CLIENT.getList(key);
|
||||
return rList.range(form, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存Set
|
||||
*
|
||||
* @param key 缓存键值
|
||||
* @param dataSet 缓存的数据
|
||||
* @return 缓存数据的对象
|
||||
*/
|
||||
public static <T> boolean setCacheSet(final String key, final Set<T> dataSet) {
|
||||
RSet<T> rSet = CLIENT.getSet(key);
|
||||
return rSet.addAll(dataSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加缓存Set数据
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param data 待缓存的数据
|
||||
* @return 缓存的对象
|
||||
*/
|
||||
public static <T> boolean addCacheSet(final String key, final T data) {
|
||||
RSet<T> rSet = CLIENT.getSet(key);
|
||||
return rSet.add(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册Set监听器
|
||||
* <p>
|
||||
* key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param listener 监听器配置
|
||||
*/
|
||||
public static <T> void addSetListener(final String key, final ObjectListener listener) {
|
||||
RSet<T> rSet = CLIENT.getSet(key);
|
||||
rSet.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的set
|
||||
*
|
||||
* @param key 缓存的key
|
||||
* @return set对象
|
||||
*/
|
||||
public static <T> Set<T> getCacheSet(final String key) {
|
||||
RSet<T> rSet = CLIENT.getSet(key);
|
||||
return rSet.readAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存Map
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param dataMap 缓存的数据
|
||||
*/
|
||||
public static <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
|
||||
if (dataMap != null) {
|
||||
RMap<String, T> rMap = CLIENT.getMap(key);
|
||||
rMap.putAll(dataMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册Map监听器
|
||||
* <p>
|
||||
* key 监听器需开启 `notify-keyspace-events` 等 redis 相关配置
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param listener 监听器配置
|
||||
*/
|
||||
public static <T> void addMapListener(final String key, final ObjectListener listener) {
|
||||
RMap<String, T> rMap = CLIENT.getMap(key);
|
||||
rMap.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的Map
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @return map对象
|
||||
*/
|
||||
public static <T> Map<String, T> getCacheMap(final String key) {
|
||||
RMap<String, T> rMap = CLIENT.getMap(key);
|
||||
return rMap.getAll(rMap.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存Map的key列表
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @return key列表
|
||||
*/
|
||||
public static <T> Set<String> getCacheMapKeySet(final String key) {
|
||||
RMap<String, T> rMap = CLIENT.getMap(key);
|
||||
return rMap.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 往Hash中存入数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKey Hash键
|
||||
* @param value 值
|
||||
*/
|
||||
public static <T> void setCacheMapValue(final String key, final String hKey, final T value) {
|
||||
RMap<String, T> rMap = CLIENT.getMap(key);
|
||||
rMap.put(hKey, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Hash中的数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKey Hash键
|
||||
* @return Hash中的对象
|
||||
*/
|
||||
public static <T> T getCacheMapValue(final String key, final String hKey) {
|
||||
RMap<String, T> rMap = CLIENT.getMap(key);
|
||||
return rMap.get(hKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Hash中的数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKey Hash键
|
||||
* @return Hash中的对象
|
||||
*/
|
||||
public static <T> T delCacheMapValue(final String key, final String hKey) {
|
||||
RMap<String, T> rMap = CLIENT.getMap(key);
|
||||
return rMap.remove(hKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Hash中的数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKeys Hash键
|
||||
*/
|
||||
public static <T> void delMultiCacheMapValue(final String key, final Set<String> hKeys) {
|
||||
RBatch batch = CLIENT.createBatch();
|
||||
RMapAsync<String, T> rMap = batch.getMap(key);
|
||||
for (String hKey : hKeys) {
|
||||
rMap.removeAsync(hKey);
|
||||
}
|
||||
batch.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多个Hash中的数据
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param hKeys Hash键集合
|
||||
* @return Hash对象集合
|
||||
*/
|
||||
public static <K, V> Map<K, V> getMultiCacheMapValue(final String key, final Set<K> hKeys) {
|
||||
RMap<K, V> rMap = CLIENT.getMap(key);
|
||||
return rMap.getAll(hKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置原子值
|
||||
*
|
||||
* @param key Redis键
|
||||
* @param value 值
|
||||
*/
|
||||
public static void setAtomicValue(String key, long value) {
|
||||
RAtomicLong atomic = CLIENT.getAtomicLong(key);
|
||||
atomic.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原子值
|
||||
*
|
||||
* @param key Redis键
|
||||
* @return 当前值
|
||||
*/
|
||||
public static long getAtomicValue(String key) {
|
||||
RAtomicLong atomic = CLIENT.getAtomicLong(key);
|
||||
return atomic.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 递增原子值
|
||||
*
|
||||
* @param key Redis键
|
||||
* @return 当前值
|
||||
*/
|
||||
public static long incrAtomicValue(String key) {
|
||||
RAtomicLong atomic = CLIENT.getAtomicLong(key);
|
||||
return atomic.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 递减原子值
|
||||
*
|
||||
* @param key Redis键
|
||||
* @return 当前值
|
||||
*/
|
||||
public static long decrAtomicValue(String key) {
|
||||
RAtomicLong atomic = CLIENT.getAtomicLong(key);
|
||||
return atomic.decrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的基本对象列表(全局匹配忽略租户 自行拼接租户id)
|
||||
* <P>
|
||||
* limit-设置扫描的限制数量(默认为0,查询全部)
|
||||
* pattern-设置键的匹配模式(默认为null)
|
||||
* chunkSize-设置每次扫描的块大小(默认为0,本方法设置为1000)
|
||||
* type-设置键的类型(默认为null,查询全部类型)
|
||||
* </P>
|
||||
* @see KeysScanOptions
|
||||
* @param pattern 字符串前缀
|
||||
* @return 对象列表
|
||||
*/
|
||||
public static Collection<String> keys(final String pattern) {
|
||||
return keys(KeysScanOptions.defaults().pattern(pattern).chunkSize(1000));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过扫描参数获取缓存的基本对象列表
|
||||
* @param keysScanOptions 扫描参数
|
||||
* <P>
|
||||
* limit-设置扫描的限制数量(默认为0,查询全部)
|
||||
* pattern-设置键的匹配模式(默认为null)
|
||||
* chunkSize-设置每次扫描的块大小(默认为0)
|
||||
* type-设置键的类型(默认为null,查询全部类型)
|
||||
* </P>
|
||||
* @see KeysScanOptions
|
||||
*/
|
||||
public static Collection<String> keys(final KeysScanOptions keysScanOptions) {
|
||||
Stream<String> keysStream = CLIENT.getKeys().getKeysStream(keysScanOptions);
|
||||
return keysStream.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存的基本对象列表(全局匹配忽略租户 自行拼接租户id)
|
||||
*
|
||||
* @param pattern 字符串前缀
|
||||
*/
|
||||
public static void deleteKeys(final String pattern) {
|
||||
CLIENT.getKeys().deleteByPattern(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查redis中是否存在key
|
||||
*
|
||||
* @param key 键
|
||||
*/
|
||||
public static Boolean hasKey(String key) {
|
||||
RKeys rKeys = CLIENT.getKeys();
|
||||
return rKeys.countExists(key) > 0;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
com.agileboot.common.redis.config.CacheConfiguration
|
||||
com.agileboot.common.redis.config.RedisConfiguration
|
||||
12
pom.xml
12
pom.xml
@ -39,6 +39,8 @@
|
||||
<org.lionsoul.version>2.6.5</org.lionsoul.version>
|
||||
<poi.version>4.1.2</poi.version>
|
||||
<io.swagger.version>1.6.8</io.swagger.version>
|
||||
<redisson.version>3.50.0</redisson.version>
|
||||
<lock4j.version>2.2.7</lock4j.version>
|
||||
|
||||
|
||||
<!-- 插件版本 -->
|
||||
@ -110,7 +112,17 @@
|
||||
<!-- <artifactId>mybatis-plus-generator</artifactId>-->
|
||||
<!-- <version>${mybatis-plus-generator.version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||
<version>${redisson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
||||
<version>${lock4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- <!– pageHelper 分页插件 –>-->
|
||||
<!-- <dependency>-->
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user