TenantDsProcessor 接入动态数据源的加载
This commit is contained in:
parent
9be666e068
commit
1b563fecaa
|
@ -18,6 +18,9 @@ import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter;
|
|||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import com.baomidou.dynamic.datasource.creator.DataSourceCreator;
|
||||
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
|
||||
import com.baomidou.dynamic.datasource.processor.DsProcessor;
|
||||
import com.baomidou.dynamic.datasource.processor.DsSpelExpressionProcessor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
|
@ -36,6 +39,7 @@ import org.springframework.data.redis.cache.RedisCacheWriter;
|
|||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Objects;
|
||||
|
||||
@AutoConfiguration
|
||||
|
@ -68,7 +72,12 @@ public class YudaoTenantAutoConfiguration {
|
|||
}
|
||||
|
||||
@Bean
|
||||
public DsProcessor dsProcessor() {
|
||||
public DsProcessor dsProcessor(
|
||||
// TenantFrameworkService tenantFrameworkService,
|
||||
// DataSource dataSource,
|
||||
// DefaultDataSourceCreator dataSourceCreator
|
||||
) {
|
||||
// TenantDsProcessor tenantDsProcessor = new TenantDsProcessor(tenantFrameworkService, dataSourceCreator);
|
||||
TenantDsProcessor tenantDsProcessor = new TenantDsProcessor();
|
||||
tenantDsProcessor.setNextProcessor(new DsSpelExpressionProcessor());
|
||||
return tenantDsProcessor;
|
||||
|
|
|
@ -1,18 +1,52 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.db.dynamic;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
|
||||
import com.baomidou.dynamic.datasource.processor.DsProcessor;
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
|
||||
import jodd.util.CollectionUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.sql.DataSource;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 基于 {@link TenantDS} 的数据源处理器
|
||||
*
|
||||
* 1. 如果有 @TenantDS 注解,返回该租户的数据源
|
||||
* 2. 如果该租户的数据源未创建,则进行创建
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class TenantDsProcessor extends DsProcessor {
|
||||
|
||||
/**
|
||||
* 用于获取租户数据源配置的 Service
|
||||
*/
|
||||
@Resource
|
||||
@Lazy
|
||||
private TenantFrameworkService tenantFrameworkService;
|
||||
|
||||
/**
|
||||
* 动态数据源
|
||||
*/
|
||||
@Resource
|
||||
@Lazy // 为什么添加 @Lazy 注解?因为它和 DynamicRoutingDataSource 相互依赖,导致无法初始化
|
||||
private DynamicRoutingDataSource dynamicRoutingDataSource;
|
||||
|
||||
/**
|
||||
* 用于创建租户数据源的 Creator
|
||||
*/
|
||||
@Resource
|
||||
@Lazy
|
||||
private DefaultDataSourceCreator dataSourceCreator;
|
||||
|
||||
@Override
|
||||
public boolean matches(String key) {
|
||||
return Objects.equals(key, TenantDS.KEY);
|
||||
|
@ -20,12 +54,33 @@ public class TenantDsProcessor extends DsProcessor {
|
|||
|
||||
@Override
|
||||
public String doDetermineDatasource(MethodInvocation invocation, String key) {
|
||||
// 获得数据源配置
|
||||
Long tenantId = TenantContextHolder.getRequiredTenantId();
|
||||
// TODO 芋艿:临时测试
|
||||
if (tenantId != 1) {
|
||||
tenantId = 2L;
|
||||
DataSourceProperty dataSourceProperty = tenantFrameworkService.getDataSourceProperty(tenantId);
|
||||
// 创建 or 创建数据源,并返回数据源名字
|
||||
return createDatasourceIfAbsent(dataSourceProperty);
|
||||
}
|
||||
|
||||
private String createDatasourceIfAbsent(DataSourceProperty dataSourceProperty) {
|
||||
// 1. 重点:如果数据源不存在,则进行创建
|
||||
if (isDataSourceNotExist(dataSourceProperty)) {
|
||||
// 问题一:为什么要加锁?因为,如果多个线程同时执行到这里,会导致多次创建数据源
|
||||
// 问题二:为什么要使用 poolName 加锁?保证多个不同的 poolName 可以并发创建数据源
|
||||
// 问题三:为什么要使用 intern 方法?因为,intern 方法,会返回一个字符串的常量池中的引用
|
||||
// intern 的说明,可见 https://www.cnblogs.com/xrq730/p/6662232.html 文章
|
||||
synchronized (dataSourceProperty.getPoolName().intern()) {
|
||||
if (isDataSourceNotExist(dataSourceProperty)) {
|
||||
DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
|
||||
dynamicRoutingDataSource.addDataSource(dataSourceProperty.getPoolName(), dataSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
return "tenant_" + tenantId + "_ds";
|
||||
// 2. 返回数据源的名字
|
||||
return dataSourceProperty.getPoolName();
|
||||
}
|
||||
|
||||
private boolean isDataSourceNotExist(DataSourceProperty dataSourceProperty) {
|
||||
return !dynamicRoutingDataSource.getDataSources().containsKey(dataSourceProperty.getPoolName());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.service;
|
||||
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -23,4 +25,12 @@ public interface TenantFrameworkService {
|
|||
*/
|
||||
void validTenant(Long id);
|
||||
|
||||
/**
|
||||
* 获得租户对应的数据源配置
|
||||
*
|
||||
* @param id 租户编号
|
||||
* @return 数据源配置
|
||||
*/
|
||||
DataSourceProperty getDataSourceProperty(Long id);
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,10 @@ package cn.iocoder.yudao.framework.tenant.core.service;
|
|||
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
|
||||
import cn.iocoder.yudao.module.infra.api.db.dto.DataSourceConfigRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.dto.TenantDataSourceConfigRespDTO;
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
@ -56,6 +59,28 @@ public class TenantFrameworkServiceImpl implements TenantFrameworkService {
|
|||
|
||||
});
|
||||
|
||||
/**
|
||||
* 针对 {@link #getDataSourceProperty(Long)} 的缓存
|
||||
*/
|
||||
private final LoadingCache<Long, DataSourceProperty> dataSourcePropertyCache = CacheUtils.buildAsyncReloadingCache(
|
||||
Duration.ofMinutes(1L), // 过期时间 1 分钟
|
||||
new CacheLoader<Long, DataSourceProperty>() {
|
||||
|
||||
@Override
|
||||
public DataSourceProperty load(Long id) {
|
||||
// 获得租户对应的数据源配置
|
||||
TenantDataSourceConfigRespDTO dataSourceConfig = tenantApi.getTenantDataSourceConfig(id);
|
||||
if (dataSourceConfig == null) {
|
||||
return null;
|
||||
}
|
||||
// 转换成 dynamic-datasource 配置
|
||||
return new DataSourceProperty()
|
||||
.setPoolName(dataSourceConfig.getName()).setUrl(dataSourceConfig.getUrl())
|
||||
.setUsername(dataSourceConfig.getUsername()).setPassword(dataSourceConfig.getPassword());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public List<Long> getTenantIds() {
|
||||
|
@ -70,4 +95,10 @@ public class TenantFrameworkServiceImpl implements TenantFrameworkService {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public DataSourceProperty getDataSourceProperty(Long id) {
|
||||
return dataSourcePropertyCache.get(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package cn.iocoder.yudao.module.infra.api.db;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.api.db.dto.DataSourceConfigRespDTO;
|
||||
|
||||
/**
|
||||
* 数据源配置 API 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface DataSourceConfigServiceApi {
|
||||
|
||||
/**
|
||||
* 获得数据源配置
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 数据源配置
|
||||
*/
|
||||
DataSourceConfigRespDTO getDataSourceConfig(Long id);
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package cn.iocoder.yudao.module.infra.api.db.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 数据源配置 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class DataSourceConfigRespDTO {
|
||||
|
||||
/**
|
||||
* 主键编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 连接名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 数据源连接
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package cn.iocoder.yudao.module.infra.api.db;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.api.db.dto.DataSourceConfigRespDTO;
|
||||
import cn.iocoder.yudao.module.infra.convert.db.DataSourceConfigConvert;
|
||||
import cn.iocoder.yudao.module.infra.service.db.DataSourceConfigService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 数据源配置 API 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
public class DataSourceConfigServiceApiImpl implements DataSourceConfigServiceApi {
|
||||
|
||||
@Resource
|
||||
private DataSourceConfigService dataSourceConfigService;
|
||||
|
||||
@Override
|
||||
public DataSourceConfigRespDTO getDataSourceConfig(Long id) {
|
||||
return DataSourceConfigConvert.INSTANCE.convert02(dataSourceConfigService.getDataSourceConfig(id));
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ import java.util.*;
|
|||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.api.db.dto.DataSourceConfigRespDTO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.db.vo.*;
|
||||
|
@ -27,4 +28,6 @@ public interface DataSourceConfigConvert {
|
|||
|
||||
List<DataSourceConfigRespVO> convertList(List<DataSourceConfigDO> list);
|
||||
|
||||
DataSourceConfigRespDTO convert02(DataSourceConfigDO bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package cn.iocoder.yudao.module.system.api.tenant;
|
||||
|
||||
import cn.iocoder.yudao.module.system.api.tenant.dto.TenantDataSourceConfigRespDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -23,4 +25,12 @@ public interface TenantApi {
|
|||
*/
|
||||
void validateTenant(Long id);
|
||||
|
||||
/**
|
||||
* 获得租户的数据源配置
|
||||
*
|
||||
* @param tenantId 租户编号
|
||||
* @return 数据源配置
|
||||
*/
|
||||
TenantDataSourceConfigRespDTO getTenantDataSourceConfig(Long tenantId);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package cn.iocoder.yudao.module.system.api.tenant;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.api.db.DataSourceConfigServiceApi;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.dto.TenantDataSourceConfigRespDTO;
|
||||
import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
|
||||
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -17,6 +21,9 @@ public class TenantApiImpl implements TenantApi {
|
|||
@Resource
|
||||
private TenantService tenantService;
|
||||
|
||||
@Resource
|
||||
private DataSourceConfigServiceApi dataSourceConfigServiceApi;
|
||||
|
||||
@Override
|
||||
public List<Long> getTenantIdList() {
|
||||
return tenantService.getTenantIdList();
|
||||
|
@ -27,4 +34,16 @@ public class TenantApiImpl implements TenantApi {
|
|||
tenantService.validTenant(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TenantDataSourceConfigRespDTO getTenantDataSourceConfig(Long tenantId) {
|
||||
// 获得租户信息
|
||||
TenantDO tenant = tenantService.getTenant(tenantId);
|
||||
if (tenant == null) {
|
||||
return null;
|
||||
}
|
||||
// 获得租户的数据源配置
|
||||
return TenantConvert.INSTANCE.convert(
|
||||
dataSourceConfigServiceApi.getDataSourceConfig(tenant.getDatasourceConfigId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package cn.iocoder.yudao.module.system.convert.tenant;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.infra.api.db.dto.DataSourceConfigRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.dto.TenantDataSourceConfigRespDTO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantCreateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExcelVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO;
|
||||
|
@ -42,4 +44,6 @@ public interface TenantConvert {
|
|||
return reqVO;
|
||||
}
|
||||
|
||||
TenantDataSourceConfigRespDTO convert(DataSourceConfigRespDTO bean);
|
||||
|
||||
}
|
||||
|
|
|
@ -79,4 +79,13 @@ public class TenantDO extends BaseDO {
|
|||
*/
|
||||
private Integer accountCount;
|
||||
|
||||
/**
|
||||
* 数据源配置编号
|
||||
*
|
||||
* 多租户采用“分库”方案时,通过该字段配置所在数据源
|
||||
*
|
||||
* 关联 DataSourceConfigDO 的 id 字段
|
||||
*/
|
||||
private Long datasourceConfigId;
|
||||
|
||||
}
|
||||
|
|
|
@ -44,15 +44,11 @@ spring:
|
|||
primary: master
|
||||
datasource:
|
||||
master:
|
||||
name: ruoyi-vue-pro
|
||||
url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
url: jdbc:mysql://400-infra.server.iocoder.cn:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: 3WLiVUBEwTbvAfsh
|
||||
slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改
|
||||
name: ruoyi-vue-pro
|
||||
url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
url: jdbc:mysql://400-infra.server.iocoder.cn:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: 3WLiVUBEwTbvAfsh
|
||||
|
||||
|
|
|
@ -44,37 +44,25 @@ spring:
|
|||
primary: master
|
||||
datasource:
|
||||
master:
|
||||
name: ruoyi-vue-pro-master
|
||||
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true # MySQL Connector/J 8.X 连接的示例
|
||||
# url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
|
||||
# url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro-master?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true # MySQL Connector/J 8.X 连接的示例
|
||||
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro-master?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
|
||||
# url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例
|
||||
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
|
||||
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.master.name} # SQLServer 连接的示例
|
||||
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro-master # SQLServer 连接的示例
|
||||
username: root
|
||||
password: 123456
|
||||
# username: sa
|
||||
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
|
||||
slave: # 模拟从库,可根据自己需要修改
|
||||
name: ruoyi-vue-pro
|
||||
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true # MySQL Connector/J 8.X 连接的示例
|
||||
# url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
|
||||
# url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true # MySQL Connector/J 8.X 连接的示例
|
||||
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
|
||||
# url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例
|
||||
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
|
||||
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.slave.name} # SQLServer 连接的示例
|
||||
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例
|
||||
username: root
|
||||
password: 123456
|
||||
# username: sa
|
||||
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
|
||||
tenant_1_ds:
|
||||
name: tenant_1
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro-tenant-a?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: 123456
|
||||
tenant_2_ds:
|
||||
name: tenant_2
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro-tenant-b?useSSL=false&serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: 123456
|
||||
|
||||
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
|
||||
redis:
|
||||
|
|
Loading…
Reference in New Issue