目的:为了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)学习总结,包括部署过程,主从复制、读写分离、主从切换等都已验证通过,可以作为完整手册使用.有需要的朋友,请拿走不谢.
上次传的代码, ...下载的朋友反映所有类都在一个文件看着有点乱,现重新整理了一下, 做成jar包使用 使用方法: 引用jar包,添加配置文件...* 2 可以选择读写分离功能,主写从读,默认不启用,都使用主服务进行所有操作
2018数据库直播大讲堂峰会-Redis专场阿里云Redis开发者午光对Redis读写分离进行介绍的PPT。
创建jedis池配置实例,redis各种crud方法,包括使用主从同步,读写分离。工具类中包括存放hash表键值对,键值对以map的方式储存,删除键值对,永久存放键值对,设置过期时间,需要的直接去gitHab上看...
- Java语言的分布式系统架构。...- 公共功能:公共功能(基类、数据访问组件、读写分离、分布式session、HTTP客户端、日志服务、队列服务、支付服务组件、redis缓存、Web安全等等)、公共配置、工具类。 适合接口编程。
Redis是一种NoSQL的文档数据库,通过key-value的结构存储在内存中,Redis读的速度是110000次/s,写的速度是81000次/s,性能很高,使用范围也很广。Redis是一个key-value存储系统。和Memcached类似,为了保证效率,...
php连接代理 提供连接池 异步pdo redis 读写分离 负载均衡 日志打印等功能
基于 gin+gorm+redis+mysql 读写分离的电子商城源码.zip
* redis主从架构的jedis客户端管理类,大概1000行代码左右 * 1 支持主从复制key/value,pop/push,pub/sub,读/写分离等功能的灾难失败自动... * 2 可以选择读写分离功能,主写从读,默认不启用,都使用主服务进行所有操作
实现了redis主从复制(读写分离)集群
基于 gin+gorm+redis+mysql 读写分离的心理咨询系统,包括 JWT 鉴权,CORS跨域,AES 对称加密,引入ELK体系,使用docker容器化部署。
下面搭建过程基于master + 多slave,实际上生产环境中redis cluster读写都是放在master上的,如果实现master + slave读写分离需要配合java客户端 jedis进一步封装,操作难度大,且没有必要,后面会在详细讲的。
1.4 Redis类似mysql可以进行主从复制,可以实现读写分离; 1.5 Redis由于是内存中数据集存储的,故对内存的要求较高,对海量数据处理有限制; 1.6 Redis主从复制时,由于宕机或其他情况一起,导致最后部分数据可能...
使用SpringBoot+Vue开发的课堂派,包含前后端代码、sql文件、文档。详情看https://blog.csdn.net/weixin_46215617/article/details/118371974 下载后按照README.md提示操作
本文涉及到近年来Redis多实例架构的演变过程,包括普通主从架构(Master、slave可进行写读分离)、哨兵模式下的主从架构、Redis-cluster高可用架构(Redis官方默认cluster下不进行读写分离)的简介。同时还介绍使用...
redis读写分离架构实践 redis哨兵架构及数据丢失问题分析 redis Cluster数据分布算法之Hash slot redis使用常见问题及性能优化思路 redis高可用及高并发实战 缓存击穿、缓存雪崩预防策略 Redis批量查询优化 ...
该文档为Redis教程, 详细介绍了 Redis 的简介、安装(Windows and Linux)、数据结构、命令、配置、持久化、主从复制, 读写分离以及Java操作Redis等内容,不仅适合没有了解过Redis 的朋友, 也适合老鸟当成参考...
【资源说明】 1、基于springboot微服务架构的班车预约系统(源码+数据库+项目说明).zip 2、该资源包括项目的全部源码,下载...6. 接下来的计划是 MySQL 读写分离、Redis 读写分离、以及分布式唯一 ID、集群管理等。
MySQL,包括MySQL的基础、事务、锁、分库分表、读写分离、MySQI优化等。Redis,包括Redis基础概念、持久化、删除策略、淘汰策略、缓存一致性、缓存击穿、缓存穿透、缓存雪崩、线程模型、事务、主从、哨兵、集群、...