目的:为了spring上下文添加读写分离的接口,可以在启动后自动注入redis只读接口或可读写接口
1. 定义一个相关的functional interface - 在spring boot的启动类里面要用到(@EnableDynamicKeyValueServiceCreation)
@Retention(value = RetentionPolicy.RUNTIME)
@Import(KeyValueServiceRegister.class)
public @interface EnableDynamicKeyValueServiceCreation {
}
2. 定义只读接口和可读写接口
public interface KeyValueReadService { String get(String key); ...}
public interface KeyValueReadWriteService extends KeyValueReadService { void set(String key, String value); ..}
3. 实现类
public class RedisConfigInfo {
private String hostName;
private int port;
private String password;
private JedisPoolConfig poolConfig;
}
public abstract class AbstractRedisOperation {
private final JedisPool jedisPool;
public AbstractRedisOperation (RedisConfigInfo info) { jedisPool = ...;}
}
public class RedisReadServiceImpl extends AbstractRedisOperation implements KeyValueReadService {
public RedisReadServiceImpl(RedisConfigInfo temp) {
super(temp);
}
@Override
public String get(String key) {
return stringValueOps.get(key);
}
}
4. 通过spring去获取redis的配置信息
@Component
public class KeyValueServiceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private static Logger logger = LoggerFactory.getLogger(KeyValueServiceRegister.class);
private Map<String, BeanDefinition> beanDefinitionMap = null;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (beanDefinitionMap == null || beanDefinitionMap.isEmpty()) {
logger.warn("beanDefinitions is empty");
return;
}
String beanName;
BeanDefinition beanDefinition;
for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
beanName = entry.getKey();
beanDefinition = entry.getValue();
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
@SuppressWarnings("rawtypes")
@Override
public void setEnvironment(Environment environment) {
String prefix = environment.getProperty("prefix.keyvalue.cache", "kv.");
Properties props = new Properties();
MutablePropertySources propSrcs = ((AbstractEnvironment) environment).getPropertySources();
StreamSupport.stream(propSrcs.spliterator(), false)
.filter(ps -> ps instanceof EnumerablePropertySource)
.map(ps -> ((EnumerablePropertySource) ps).getPropertyNames())
.flatMap(Arrays::<String>stream).filter(name -> name.startsWith(prefix)).forEach(
propName -> props.setProperty(propName, environment.getProperty(propName)));
beanDefinitionMap = Collections
.unmodifiableMap(KeyValueServiceHelper.buildBeanDefinitionMap(props));
}
}
5. 根据配置信息构建bean
public class KeyValueServiceHelper {
private static final String POSFIX_READ = ".r";
private static final String POSFIX_WRITE = ".w";
public static final Map<String, Properties> buildDataSourceMap(Properties prop) {
if (prop == null || prop.isEmpty()) {
return Collections.emptyMap();
}
Map<String, Properties> result = new HashMap<>();
Set<String> keys = prop.stringPropertyNames();
Properties temp;
String dsName;
for (String key : keys) {
dsName = key;
if (!isKeyValueServiceBeanName(key)) {
dsName = removePosfix(key);
}
temp = result.get(dsName);
if (temp == null) {
temp = new Properties();
result.put(dsName, temp);
}
temp.setProperty(key, prop.getProperty(key));
}
return result;
}
public static final boolean isKeyValueServiceBeanName(String str) {
if (StringUtils.isBlank(str)) {
return false;
}
return str.endsWith(POSFIX_READ) || str.endsWith(POSFIX_WRITE);
}
public static final String removePosfix(String str) {
if (StringUtils.isBlank(str)) {
return str;
}
int idx = str.lastIndexOf(".");
if (idx == -1) {
return str;
}
return str.substring(0, idx);
}
public static final String resolveBeanClassName(final String beanName,
final String connectionStr) {
if (StringUtils.isBlank(beanName)) {
throw new IllegalArgumentException("bean name is blank");
}
if (StringUtils.isBlank(connectionStr)) {
throw new IllegalArgumentException("connectionStr is blank");
}
if (!RedisConnectionHelper.isRedisConnectionString(connectionStr)) {
throw new IllegalArgumentException("connectionStr is invalid");
}
if (beanName.endsWith(POSFIX_READ)) {
return RedisReadServiceImpl.class.getName();
}
if (beanName.endsWith(POSFIX_WRITE)) {
return RedisReadWriteServiceImpl.class.getName();
}
throw new IllegalArgumentException("beanName is invalid,should end with '.r' or '.w'");
}
public static final Map<String, BeanDefinition> buildBeanDefinitionMap(Properties props) {
Map<String, Properties> cacheSources = buildDataSourceMap(props);
if (cacheSources == null || cacheSources.isEmpty()) {
return Collections.emptyMap();
}
BeanDefinition def;
String beanName;
String connectionStr;
String beanClassName;
Properties prop;
RedisConfigInfo info;
Map<String, BeanDefinition> beanMap =
new HashMap<String, BeanDefinition>(cacheSources.size());
for (Map.Entry<String, Properties> entry : cacheSources.entrySet()) {
beanName = entry.getKey();
prop = entry.getValue();
connectionStr = prop.getProperty(beanName);
if (!RedisConnectionHelper.isRedisConnectionString(connectionStr)) {
continue;
}
info = RedisConnectionHelper.parseRedisConfigInfo(prop, beanName);
def = new GenericBeanDefinition();
beanClassName = KeyValueServiceHelper.resolveBeanClassName(beanName, connectionStr);
def.setBeanClassName(beanClassName);
def.getConstructorArgumentValues().addGenericArgumentValue(info);
beanMap.put(beanName, def);
}
return beanMap;
}
}
6. 如何使用
a. 在SpringBoot的启动类里面加上@EnableDynamicKeyValueServiceCreation
b. 配置文件里面加上读写分离的redis的配置信息 - 读的是.r结尾,可写的是.w结尾
c. 在需要只读redis的service里面, 直接注入(@KeyValueReadService (name=**.r)
d. 在需要读写redis的service里面, 直接注入(@KeyValueReadService (name=**.w)
相关推荐
简单的redis读写分离demo 用的是依赖注入的方式,redis用的是主从复制
Redis Sentinel提供了高可用性和自动化故障恢复能力,通过主从复制保证数据安全,读写分离提升系统性能,哨兵监控与故障转移保证服务稳定性。通过正确配置和部署,可以构建一个可靠的Redis集群,确保业务连续性。在...
上次传的代码, ...下载的朋友反映所有类都在一个文件看着有点乱,现重新整理了一下, 做成jar包使用 使用方法: 引用jar包,添加配置文件...* 2 可以选择读写分离功能,主写从读,默认不启用,都使用主服务进行所有操作
2018数据库直播大讲堂峰会-Redis专场阿里云Redis开发者午光对Redis读写分离进行介绍的PPT。
在本项目中,Redis被用作集群实现读写分离,以提高系统的响应速度和数据处理能力。 首先,Vue.js是一个轻量级的前端JavaScript框架,它提供了组件化开发模式,使得开发者能够高效地构建用户界面。Vue的特点包括虚拟...
在本文中,我们将深入探讨如何在Java环境中,特别是在Spring框架下,实现全面的Redis线程池配置,包括Redis Cluster线程池、主从Redis的读写分离以及Sentinel的读写分离策略。 首先,我们要理解Redis线程池的重要性...
5. **性能优化**:除了基本的读写分离,还可以考虑使用缓存(如Redis)、分页查询、索引优化等手段进一步提升系统性能。 通过深入研究这个项目源码,开发者不仅可以学习到读写分离技术,还能加深对SSM框架的理解,...
在IT行业中,数据库的读写分离是一种常见的优化策略,它能有效地提高系统性能,尤其是在高并发环境下。SpringMVC作为Java Web开发中的一个强大框架,能够很好地支持这种架构设计。以下将详细介绍如何在SpringMVC项目...
Jedis 是 Redis 官网首选的 Java 客户端开发包,通过 Jedis,我们可以在 Java 中使用 Redis。Jedis 的使用非常简单,首先需要引入相关的 jar 包,然后创建连接实例,最后使用 Jedis 操作 Redis。 Redis 的特点 ...
在实际应用中,实现 Redis 的读写分离可以通过编程的方式,如在 Java 应用中使用 AOP(面向切面编程)来实现。例如,可以定义两个注解 `@JedisPoolSelector`,一个用于标记写操作,另一个用于标记读操作。然后创建一...
为了提高读取性能,可以配置主从复制,实现读写分离。 8. **注意点**: - 不支持某些命令:如`keys`、`sort`、`watch`等在集群模式下不完全支持,因为它们可能涉及到跨节点的操作。 - 集群的初始化和扩展需要谨慎...
创建jedis池配置实例,redis各种crud方法,包括使用主从同步,读写分离。工具类中包括存放hash表键值对,键值对以map的方式储存,删除键值对,永久存放键值对,设置过期时间,需要的直接去gitHab上看...
5. **读写分离** - 读请求直接命中Redis,写请求通过Java应用程序处理MySQL,实现数据库读写的解耦。 6. **错误处理与回源** - 当Redis无数据或出现故障时,Java应用程序需要有回源机制,即直接查询MySQL并更新...
MyBatis可以使用简单的XML或注解进行配置和原始映射,将接口和Java的POJOs(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。 3. **多数据源**:在大型系统中,数据库通常会被分为多个数据源,每个...
Redis是一种NoSQL的文档数据库,通过key-value的结构存储在内存中,Redis读的速度是110000次/s,写的速度是81000次/s,性能很高,使用范围也很广。Redis是一个key-value存储系统。和Memcached类似,为了保证效率,...
4. **主从复制与集群**: Redis支持主从复制,可以实现数据备份和读写分离。Redis Cluster是其官方提供的分布式解决方案,能自动将数据分散在多个节点上,实现水平扩展。 5. **高级功能**: Redis还提供了发布/订阅、...
总结起来,这个项目结合了Gin的Web开发能力、GORM的数据库操作便利性、Redis的高性能缓存和队列服务,以及MySQL的读写分离策略,构建了一个功能完善的电子商务平台。通过合理的架构设计和数据库优化,项目能够有效地...
主从复制允许将一台Redis服务器的数据复制到多个从服务器,实现数据的备份和读写分离,提高系统的可用性和可扩展性。 哨兵模式是Redis的一种高可用解决方案,它可以在主服务器故障时,自动将从服务器提升为新的主...
4. **主从复制与集群**:通过主从复制机制,Redis支持数据的热备份以及读写分离,从而提高系统的可用性和负载能力;此外,Redis集群提供了自动分片功能,进一步增强了系统的扩展性。 5. **Lua脚本支持**:为了减少...
这个工具包包含了多种功能模块,如读写分离、多数据源支持,以及对Mybatis、Sharding-JDBC、Redis和Seata等流行技术的集成。 1. **Spring框架**:Spring是Java开发中最广泛使用的应用框架之一,提供了依赖注入(DI...