[feat] 新增Redis多数据源插件 magic-api-plugin-dynamic-redis

This commit is contained in:
冰点 2024-06-17 17:21:15 +08:00
parent 662646a8c2
commit f46d9c77e3
6 changed files with 28 additions and 135 deletions

View File

@ -4,10 +4,11 @@ package org.ssssssss.magicapi.redis;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@ -22,14 +23,16 @@ import java.util.Map;
@Slf4j
@EnableAsync
@Configuration
@DependsOn("redisInstanceFactory")
@ConfigurationProperties(prefix = "magic-api.redis")
@Import({MagicRedisConfiguration.class,RedisInstanceFactory.class,MagicRedisConfiguration.class})
@Import({RedisInstanceFactory.class,MagicRedisConfiguration.class,})
public class MagicDynamicRedisAutoConfig implements InitializingBean {
@Getter
public static String primary;
@Autowired
public RedisInstanceFactory redisInstanceFactory;
@Getter
private static Map<String, MagicRedisProperties> dynamic = new LinkedHashMap<>();
@ -37,25 +40,26 @@ public class MagicDynamicRedisAutoConfig implements InitializingBean {
public void afterPropertiesSet() {
Assert.isNotNull(primary, "Redis未设置默认库");
dynamic.forEach((redisName, redisProperty) -> {
// 构建Redis服务
RedisInstanceFactory.getInstance().buildRedisTemplate(redisName, redisProperty);
redisInstanceFactory.buildRedisTemplate(redisName, redisProperty);
});
log.info("[动态Redis]--共创建Redis[{}]个", dynamic.size());
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate() {
MagicRedisProperties redisProperties = dynamic.get(primary);
RedisConnectionFactory connectionFactory = RedisInstanceFactory.getInstance().buildLettuceConnectionFactory(primary, redisProperties);
RedisConnectionFactory connectionFactory = redisInstanceFactory.buildLettuceConnectionFactory(primary, redisProperties);
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(connectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public RedisTemplate<Object, Object> redisTemplate() {
MagicRedisProperties redisProperties = dynamic.get(primary);
RedisConnectionFactory connectionFactory = RedisInstanceFactory.getInstance().buildLettuceConnectionFactory(primary, redisProperties);
RedisConnectionFactory connectionFactory = redisInstanceFactory.buildLettuceConnectionFactory(primary, redisProperties);
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
return template;

View File

@ -21,7 +21,7 @@ public class MagicRedisProperties {
/**
* 连接类型
*/
private RedisConnectionType type;
private String type;
/**
* redis主机地址ipport多个用逗号(,)分隔

View File

@ -1,20 +1,8 @@
package org.ssssssss.magicapi.redis;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisPipelineException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ReflectionUtils;
import org.ssssssss.magicapi.core.annotation.MagicModule;
import org.ssssssss.magicapi.utils.Assert;
import org.ssssssss.script.functions.DynamicMethod;
import org.ssssssss.script.reflection.JavaReflection;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
/**
* redis 多数据源插件模块
@ -23,12 +11,10 @@ import java.util.*;
*/
@MagicModule("dynamicRedis")
@Slf4j
public class RedisDynamicModule implements DynamicMethod {
public class RedisDynamicModule {
private final RedisInstanceFactory redisInstanceFactory;
private RedisTemplate redisTemplate;
private boolean isRedisson;
public RedisDynamicModule(RedisInstanceFactory redisInstanceFactory) {
this.redisInstanceFactory = redisInstanceFactory;
@ -37,110 +23,19 @@ public class RedisDynamicModule implements DynamicMethod {
/**
* 序列化
*/
public RedisDynamicModule name(String name) {
this.redisTemplate = redisInstanceFactory.getRedisTemplate(name, false);
Assert.isTrue(redisTemplate==null, "当前没有配置redis " + name);
this.isRedisson = Objects.equals("org.redisson.spring.data.connection.RedissonConnectionFactory", this.redisTemplate.getConnectionFactory().getClass().getName());
return this;
}
/**
* 序列化
*/
private byte[] serializer(Object value) {
if (value == null || value instanceof String) {
return redisTemplate.getStringSerializer().serialize((String) value);
}
return serializer(value.toString());
}
private Object serializerForRedisson(Object value) {
if (value == null || JavaReflection.isPrimitiveAssignableFrom(value.getClass(), value.getClass())) {
return value;
}
return serializer(value.toString());
}
/**
* 反序列化
*/
@SuppressWarnings("unchecked")
private Object deserialize(Object value) {
if (value != null) {
if (value instanceof byte[]) {
return this.redisTemplate.getStringSerializer().deserialize((byte[]) value);
}
if (value instanceof List) {
List<Object> valueList = (List<Object>) value;
List<Object> resultList = new ArrayList<>(valueList.size());
for (Object val : valueList) {
resultList.add(deserialize(val));
}
return resultList;
}
if (value instanceof Map) {
Map<Object, Object> map = (Map<Object, Object>) value;
LinkedHashMap<Object, Object> newMap = new LinkedHashMap<>(map.size());
map.forEach((key, val) -> newMap.put(deserialize(key), deserialize(val)));
return newMap;
}
}
return value;
}
/**
* 执行命令
*
* @param methodName 命令名称
* @param parameters 命令参数
*/
@Override
public Object execute(String methodName, List<Object> parameters) {
return this.redisTemplate.execute(connection -> {
Object result;
if (isRedisson) {
result = executeForRedisson(((DefaultStringRedisConnection) connection).getDelegate(), methodName, parameters);
public RedisTemplate<?, ?> of(String name) {
RedisTemplate<?, ?> redisTemplate = redisInstanceFactory.getRedisTemplate(name, true);
if (redisTemplate!=null){
log.info("使用redis {}", name);
}else {
byte[][] params = new byte[parameters.size()][];
for (int i = 0; i < params.length; i++) {
params[i] = serializer(parameters.get(i));
log.error("当前没有配置redis {}", name);
}
result = connection.execute(methodName, params);
}
return deserialize(result);
}, isRedisson || this.redisTemplate.isExposeConnection());
return redisTemplate;
}
private Object executeForRedisson(RedisConnection connection, String command, List<Object> parameters) {
Method[] methods = connection.getClass().getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equalsIgnoreCase(command) && Modifier.isPublic(method.getModifiers()) && method.getParameterTypes().length == parameters.size()) {
try {
Object ret = this.execute(connection, method, parameters);
if (ret instanceof String) {
return ((String) ret).getBytes();
}
return ret;
} catch (IllegalArgumentException e) {
if (connection.isPipelined()) {
throw new RedisPipelineException(e);
public RedisTemplate<?, ?> name(String name) {
return of(name);
}
throw new InvalidDataAccessApiUsageException(e.getMessage(), e);
}
}
}
throw new UnsupportedOperationException();
}
private Object execute(RedisConnection connection, Method method, List<Object> parameters) {
if (method.getParameterTypes().length > 0 && method.getParameterTypes()[0] == byte[][].class) {
return ReflectionUtils.invokeMethod(method, connection, parameters.stream().map(this::serializer).toArray(byte[][]::new));
} else if (parameters.size() == 0) {
return ReflectionUtils.invokeMethod(method, connection);
}
return ReflectionUtils.invokeMethod(method, connection, parameters.stream().map(this::serializerForRedisson).toArray());
}
}

View File

@ -50,15 +50,7 @@ public class RedisInstanceFactory implements ApplicationContextAware {
private static ApplicationContext applicationContext;
private RedisInstanceFactory() {
}
public static class RedisFactoryLoader {
private static final RedisInstanceFactory INSTANCE = new RedisInstanceFactory();
}
public static RedisInstanceFactory getInstance() {
return RedisFactoryLoader.INSTANCE;
public RedisInstanceFactory() {
}
public RedisTemplate<?, ?> getRedisTemplate(String redisName, boolean isStringRedisTemplate) {
@ -83,7 +75,7 @@ public class RedisInstanceFactory implements ApplicationContextAware {
* 创建 ConnectionFactory
*/
public RedisConnectionFactory buildLettuceConnectionFactory(String redisName, MagicRedisProperties redisProperties) {
String redisClientName = getRedisTemplateName(redisName, false);
String redisClientName = redisName+"RedisConnectionFactory";
if (applicationContext.containsBean(redisClientName)) {
return getBean(redisClientName);
}
@ -115,7 +107,7 @@ public class RedisInstanceFactory implements ApplicationContextAware {
.poolConfig(poolConfig)
.build();
LettuceConnectionFactory lettuceConnectionFactory = null;
RedisConnectionType connectionType = redisProperties.getType();
RedisConnectionType connectionType = RedisConnectionType.match(redisProperties.getType());
if (RedisConnectionType.SENTINEL.equals(connectionType)) {
lettuceConnectionFactory = new LettuceConnectionFactory(buildSentinelConfig(redisProperties), clientConfig);
} else if (RedisConnectionType.CLUSTER.equals(connectionType)) {

View File

@ -1 +1,2 @@
org.ssssssss.magicapi.redis.MagicRedisConfiguration
org.ssssssss.magicapi.redis.RedisInstanceFactory
org.ssssssss.magicapi.redis.MagicDynamicRedisAutoConfig

View File

@ -18,6 +18,7 @@
<module>magic-api-plugin-swagger</module>
<module>magic-api-plugin-springdoc</module>
<module>magic-api-plugin-redis</module>
<module>magic-api-plugin-dynamic-redis</module>
<module>magic-api-plugin-mongo</module>
<module>magic-api-plugin-elasticsearch</module>
<module>magic-api-plugin-cluster</module>