csdn博客:https://blog.csdn.net/Hellowenpan?spm=1000.2115.3001.5343
该模块是基于spring-boot-starter-data-redis(spring-boot-starter-parent版本2.4.8),并且对spring-boot-starter-data-redis模块部分功能增强。
项目中有些功能提供了多种使用方式,建议使用最优雅的方式去使用。这些使用方式也是在开发过程中对代码一步步优化而来,所以说最后提供的使用方式一定都是最优的。
mvn clean install
<dependency>
<groupId>org.basis.enhance</groupId>
<artifactId>enhance-boot-data-redis</artifactId>
<!-- 排除lettuce依赖,data-redis默认选择lettuce -->
<!--<exclusions>-->
<!-- <exclusion>-->
<!-- <groupId>io.lettuce</groupId>-->
<!-- <artifactId>lettuce-core</artifactId>-->
<!-- </exclusion>-->
<!--</exclusions>-->
<version>1.0-SNAPSHOT</version>
</dependency>
<!--如果redis客户端需要使用jedis,pom文件里需要引入jedis的依赖,并排除lettuce依赖-->
<!--
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
-->
具体功能对应的具体配置在使用示例讲解的时候会详细说明!!!
stone:
redis:
# 开启动态切换redis db【可选,默认不开启】
dynamic-database: true
动态切换db功能封装在RedisHelper
中,同时在RedisHelper
对Redis的常用操作也做了封装,如果是使用的常见方法,则直接调用RedisHelper中的相关方法即可,使用上非常的方便。
application.yml文件中开启动态切换db配置并配置Redis数据源,这里只使用一个默认的Redis数据源做演示,多数据源后面演示。
spring:
redis:
# 配置使用的默认数据源
host: ${SPRING_REDIS_HOST:wenpan-host}
port: ${SPRING_REDIS_PORT:6379}
password: ${SPRING_REDIS_PASSWORD:WenPan@123}
database: ${SPRING_REDIS_DATABASE:1}
client-type: lettuce
lettuce:
pool:
max-active: ${SPRING_REDIS_POOL_MAX_ACTIVE:16}
max-idle: ${SPRING_REDIS_POOL_MAX_IDLE:16}
max-wait: ${SPRING_REDIS_POOL_MAX_WAIT:5000}
stone:
redis:
# 开启动态切换redis db
dynamic-database: true
使用演示
@Slf4j
@RestController("TestEncryptController.v1")
@RequestMapping("/v1/test-enhance-redis")
public class TestEnhanceDataRedisController {
@Autowired
@Qualifier("redisHelper")
private RedisHelper redisHelper;
public void testChangeDb() {
// 设置当前线程需要将数据写入到几号db
redisHelper.setCurrentDatabase(2);
// 写到2号库(这里调用自己封装的方法,如果要使用原生的方法并写入自定义db,后面介绍)
redisHelper.strSet("dynamic-key-test-1", "value-1");
// 清除当前线程所使用的db信息
redisHelper.clearCurrentDatabase();
}
}
如果提供的RedisHelper的封装方法中没有你想要使用的方法,你想要调用原生的redisTemplate的方法,并且实现自定义db写入,那么怎么办呢?直接调用redisHelper.getRedisTemplate()
即可获取RedisTemplate,然后进行操作即可。
@Slf4j
@RestController("TestEncryptController.v1")
@RequestMapping("/v1/test-enhance-redis")
public class TestEnhanceDataRedisController {
@Autowired
@Qualifier("redisHelper")
private RedisHelper redisHelper;
public void testChangeDb() {
// 设置当前线程需要将数据写入到几号db
redisHelper.setCurrentDatabase(2);
// 使用原生的方法并写入自定义db
RedisTemplate<String, String> redisTemplate = redisHelper.getRedisTemplate();
redisTemplate.opsForValue().set("key","value");
// 清除当前线程所使用的db信息
redisHelper.clearCurrentDatabase();
}
}
上面的使用可能你觉得有点麻烦,每次都要手动的去set db 和清除db,那么能不能省去这两部操作更简单的使用呢?可以的,直接通过提供的EasyRedisHelper
来操作即可,EasyRedisHelper
提供多种便捷使用的方式,这里以一种举例。
@Slf4j
@RestController("TestEncryptController.v1")
@RequestMapping("/v1/test-enhance-redis")
public class TestEnhanceDataRedisController {
@Autowired
@Qualifier("redisHelper")
private RedisHelper redisHelper;
@GetMapping("/test-")
public void testChangeDb6() {
// 指定操作库,不带返回值的操作,使用redisHelper封装的api
EasyRedisHelper.execute(2, () -> redisHelper.lstLeftPush("key", "value"));
// 指定操作库,带返回值的操作,使用redisHelper封装的api
String result = EasyRedisHelper.executeWithResult(2, () -> redisHelper.strGet("dynamic-key-test-2"));
}
}
@Slf4j
@RestController("TestEncryptController.v1")
@RequestMapping("/v1/test-enhance-redis")
public class TestEnhanceDataRedisController {
@Autowired
@Qualifier("redisHelper")
private RedisHelper redisHelper;
@GetMapping("/test-7")
public void testChangeDb7() {
// 指定操作库,不带返回值的操作,使用redisTemplate原生api
EasyRedisHelper.execute(1, (redisTemplate) -> redisTemplate.opsForList().leftPush("key", "value"));
// 指定操作库,带返回值的操作,使用redisTemplate原生api
String result = EasyRedisHelper.executeWithResult(1,
(redisTemplate) -> redisTemplate.opsForList().leftPop("queue"));
}
}
前三种使用方式都比较麻烦,要不就是需要手动设置db要不就是使用lamda表达式,使用上不是特别友好。这里推荐使用这种方式来动态的切换db(即直接使用redisHelper.opsXXXDb
)。
@Slf4j
@RestController("TestChangeDbController.v1")
@RequestMapping("/v1/test-change-db")
public class TestChangeDbController {
@Autowired
private RedisHelper redisHelper;
@GetMapping("/test-01")
public void test01() {
// 操作1号db
redisHelper.opsDbOne().opsForValue().set(getRandomValue(), getRandomValue());
// 操作2号db
redisHelper.opsDbTwo().opsForValue().set(getRandomValue(), getRandomValue());
// 操作3号db
redisHelper.opsDbThree().opsForValue().set(getRandomValue(), getRandomValue());
// 操作4号db
redisHelper.opsDbFour().opsForValue().set(getRandomValue(), getRandomValue());
}
private String getRandomValue() {
return UUID.randomUUID().toString();
}
}
==所以,这里一共提供了四种切换db的使用方式,可以选择任意一种进行使用。推荐使用第④种,最为简单直观。==
该增强插件中也提供了对于Redis队列消息的消费增强,我们不用再写代码去手动的 pull Redis队列的消息,而是当Redis队列中有消息时自动传递给我们。我们只需要实现指定的接口 + 注解
标注需要监控的队列名称即可。
注意该队列监控的是默认的Redis数据源中的队列,多数据源的情况下也是监控的默认的Redis数据源中的队列。
使用场景描述:
test-queue
队列,当该队列中有消息的时候我们需要及时的将该消息取出来进行消费。我们只需要如下做即可。stone:
redis:
redis-queue: true
需要使用
@QueueHandler
注解指定需要监听的队列名称。
@Slf4j
@QueueHandler("test-queue")
public class RedisQueueTestServiceImpl implements IQueueHandler {
@Override
public void handle(String message) {
log.info("获取到redis的test-queue队列的消息,消息内容是:{}", message);
// do something.....
}
}
当test-queue
队列中有消息时,会回调handle
方法将test-queue
队列中取出的值以参数的形式传递给handle
方法。我们只需要在该方法中对获取到的消息数据做操作即可。
在项目开发中我们可能会使用到多个Redis数据源,在该项目中也做了多数据源的实现,并且每个数据源都可以动态的切换db进行操作。
使用场景描述:
192.168.1.100
和192.168.1.101
192.168.1.100
数据源,B会员的数据存放到192.168.1.101
@SpringBootApplication
@EnableConfigurationProperties
// 开启redis多数据源使用
@EnableRedisMultiDataSource
public class EnhanceDataRedisDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EnhanceDataRedisDemoApplication.class, args);
}
}
spring:
application:
name: enhance-data-redis-demo
redis:
# 默认数据源
host: ${SPRING_REDIS_HOST:wenpan-host}
port: ${SPRING_REDIS_PORT:6379}
password: ${SPRING_REDIS_PASSWORD:WenPan@123}
database: ${SPRING_REDIS_DATABASE:0}
client-type: lettuce
lettuce:
pool:
max-active: ${SPRING_REDIS_POOL_MAX_ACTIVE:16}
max-idle: ${SPRING_REDIS_POOL_MAX_IDLE:16}
max-wait: ${SPRING_REDIS_POOL_MAX_WAIT:5000}
datasource:
# 第一个数据源
source1:
host: ${SPRING_REDIS_HOST:wenpan-host}
port: ${SPRING_REDIS_PORT:6379}
password: ${SPRING_REDIS_PASSWORD:WenPan@123}
database: ${SPRING_REDIS_DATABASE:1}
# 第二个数据源
source2:
host: ${SPRING_REDIS_HOST:yuanping-host}
port: ${SPRING_REDIS_PORT:6379}
password: ${SPRING_REDIS_PASSWORD:WenPan@123}
database: ${SPRING_REDIS_DATABASE:2}
stone:
redis:
# 开启多数据源动态切换redis db
dynamic-database: true
操作默认数据源,直接注入
RedisHelper
即可。
@Slf4j
@RestController("TestEncryptController.v1")
@RequestMapping("/v1/test-enhance-redis")
public class TestEnhanceDataRedisController {
/**
* 注入默认数据源的redisTemplate
*/
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 默认数据源对应的redisHelper
*/
@Autowired
@Qualifier("redisHelper")
private RedisHelper redisHelper;
@GetMapping("/test-default")
public void testDefaultRedisTemplate() {
// 使用默认数据源的redisTemplate操作默认数据源
redisTemplate.opsForValue().set("key", "value");
// 使用默认数据源的redisHelper操作默认数据源
redisHelper.lstRightPop("key");
// 使用默认数据源的redisHelper动态切换db
try {
redisHelper.setCurrentDatabase(2);
redisHelper.lstRightPop("key");
} finally {
redisHelper.clearCurrentDatabase();
}
}
}
// 多数据源情况下,操作默认数据源并动态切换db测试
@GetMapping("/test-100")
public void test100() {
// 使用多数据源客户端操作默认数据源的指定db
// 操作默认的数据源的1号db
multisourceClient.opsDbOne(DEFAULT_SOURCE).opsForValue().set(getRandomValue(), getRandomValue());
// 操作默认的数据源的2号db
multisourceClient.opsDbTwo(DEFAULT_SOURCE).opsForValue().set(getRandomValue(), getRandomValue());
// 操作默认的数据源的3号db
multisourceClient.opsDbThree(DEFAULT_SOURCE).opsForValue().set(getRandomValue(), getRandomValue());
// 使用redisHelper操作默认数据源的指定db
// 操作默认的数据源的1号db
redisHelper.opsDbOne().opsForValue().get("key");
// 操作默认的数据源的2号db
redisHelper.opsDbTwo().opsForValue().get("key");
}
①、指定数据源名称注入指定的数据源
@Slf4j
@RestController("TestEncryptController.v1")
@RequestMapping("/v1/test-enhance-redis")
public class TestEnhanceDataRedisController {
/**
* 注入第一个数据源
*/
@Autowired
@Qualifier("source1RedisTemplate")
private RedisTemplate<String, String> source1RedisTemplate;
/**
* source1数据源对应的redisHelper
*/
@Autowired
@Qualifier("source1RedisHelper")
private RedisHelper source1RedisHelper;
@GetMapping("/test-source1-template")
public void testSource1RedisTemplate() {
// 使用source1数据源的redisTemplate操作source1数据源
source1RedisTemplate.opsForValue().set("key", "value");
// 使用source1数据源的redisHelper操作source1数据源(切换db操作)
EasyRedisHelper.execute(2, () -> source1RedisHelper.lstLeftPush("key", "value"));
}
}
以上使用方式一使用起来都较为复杂,不是特别友好(比如:我们需要手动的使用@Qualifier
注解指定容器中bean的名称进行注入),这里提供一种更加友好的使用方式RedisMultisourceClient
,在RedisMultisourceClient
中提供了丰富的易用的对于多数据源和动态切换db的操作。
@Slf4j
@RestController("TestMultiDataSourceController.v1")
@RequestMapping("/v1/test-multi-source")
public class TestMultiDataSourceController {
@Autowired
private RedisMultisourceClient multisourceClient;
@Autowired
private RedisHelper redisHelper;
/**
* 操作指定的数据源的指定db
*/
@GetMapping("/test-1")
public void test01() {
String key = "test-" + UUID.randomUUID().toString();
String value = "value-" + UUID.randomUUID().toString();
log.info("key = {}, value = {}", key, value);
// 写入source1数据源的1号库
multisourceClient.opsDbOne("source1").opsForValue().set(key, value);
// 写入source2数据源的1号库
multisourceClient.opsDbOne("source2").opsForValue().set(key, value);
// 写入source1数据源的2号库
multisourceClient.opsDbOne("source1").opsForValue().set(key, value);
// 写入source2数据源的2号库
multisourceClient.opsDbOne("source2").opsForValue().set(key, value);
}
/**
* 操作默认数据源的指定db
*/
@GetMapping("/test-01")
public void test01() {
// 操作1号db
redisHelper.opsDbOne().opsForValue().set(getRandomValue(), getRandomValue());
// 操作2号db
redisHelper.opsDbTwo().opsForValue().set(getRandomValue(), getRandomValue());
// 操作3号db
redisHelper.opsDbThree().opsForValue().set(getRandomValue(), getRandomValue());
// 操作4号db
redisHelper.opsDbFour().opsForValue().set(getRandomValue(), getRandomValue());
}
/**
* 使用多数据源客户端操作默认数据源并且动态切换db
*/
@GetMapping("/test-100")
public void test100() {
// 操作默认的数据源的1号db
multisourceClient.opsDbOne(DEFAULT_SOURCE).opsForValue().set(getRandomValue(), getRandomValue());
// 操作默认的数据源的2号db
multisourceClient.opsDbTwo(DEFAULT_SOURCE).opsForValue().set(getRandomValue(), getRandomValue());
// 操作默认的数据源的3号db
multisourceClient.opsDbThree(DEFAULT_SOURCE).opsForValue().set(getRandomValue(), getRandomValue());
}
}
RedisMultiSourceClient
方便的操作。该增强组件也实现了对于redis的发布订阅模式的使用进行了增强,但是这里在实现上指定订阅一个指定的channel
,无法实现动态的指定多个channel的模式,所以意义不是很大。但可以作为学习参考。
该增强组件中提供了其他的对redis的相关操作的工具类封装,可参考源代码自由使用!
该增强组件是对于每个redis db
创建一个redisTemplate
并缓存起来,在使用的时候通过db值来动态的选取对应的redisTemplate
来进行调用对应的方法,不会有安全性问题。
redisTemplate
创建的时候使用类似懒加载的方式,项目启动时并不会创建多个redis连接工厂,当需要动态切换redis db的时候如果不存在对该db的redisTemplate
则才去创建redisTemplate
并缓存起来,不会有太大的连接开销和性能损耗。
创建redisTemplate需要构建redisConnectionFactory、lettuce连接配置或jedis连接配置,这些配置的创建是一个十分复杂且麻烦的事情,我们采用了和源码相同的创建方式,仅仅对spring-boot-starter-data-redis
中创建LettuceConnectionConfigure
、JedisConnectionConfigure
、redisConnectionFactory
做了自定义更改。所以原则上还是使用的spring-boot-data-redis
构建redisTemplate的原流程进行redisTemplate的构建,所以构建的redisTemplate一般不会有隐藏bug。
该增强组件支持jedis和lettuce客户端和spring-boot-starter-data-redis
保持一致,且默认是使用lettuce客户端的,用户可以无感知的切换到jedis客户端。
支持的Redis部署模式和spring-boot-starter-data-redis
保持一致(可支持单机版、集群模式、哨兵模式)
该增强组件默认为容器注入RedisTemplate
和StringRedisTemplate
两个template,且这两个template使用的key-value序列化方式都是使用stringRedisSerializer
。使用者直接注入这俩template即可使用。如果使用者想采用自己定义的key-value序列化器来创建template,那么只需要自己在配置文件中使用@Bean
的方式注入自定义的RedisTemplate即可覆盖默认提供的RedisTemplate(即有用户配置的RedisTemplate则优先使用用户配置的RedisTemplate,如果没有则使用组件默认提供的RedisTemplate)。
创建动态RedisTemplate的时候目前创建的RedisTemplate的key-value序列化器默认使用的是stringRedisSerializer
,这里以后应该实现成用于动态可配置。
最核心的是动态切换数据源的思路,Redis队列监控、多数据源、发布订阅监控这些思路都比较简单而且比较容易实现。所以着重介绍一下动态切换redis db的思路。
这里只介绍整个流程实现的核心原理,具体实现细节忽略!!!
从原理上讲很简单,首先我们要操作不同的db,且该db要由用户动态可指定,我们知道RedisTemplate在创建的时候默认使用的是用户在application.yml中使用database
指定的db,如下
spring:
redis:
host: ${SPRING_REDIS_HOST:wenpan-host}
port: ${SPRING_REDIS_PORT:6379}
password: ${SPRING_REDIS_PASSWORD:WenPan@123}
# 指定使用Redis几号库
database: ${SPRING_REDIS_DATABASE:1}
那么要更改所使用的db就意味着在使用的时候要更改RedisTemplate连接,使原本连向1号库的RedisTemplate重新和redis进行连接,所以最开始我考虑了如下方案
如果使用上面的第二步,那么会有很大问题
RedisTemplate
的办法行不通,那么可不可以考虑使用多个RedisTemplate呢(懒创建)?对于每个库我都通过redisConnectionFactory
去创建一个连接和一个对应的RedisTemplate
,并且缓存起来。注意:该代码的实现中为了提供更加友好的使用方式,在使用方式实现上参考了RedisTemplate
+ DefaultValueOperations
来进行实现。
@Component
肯定是不行的,因为我们要在容器启动的过程中动态的向容器注入每个数据源对应的RedisTemplate和RedisHelper。使用@Component
等注解无法实现根据配置文件动态的创建bean。所以我们考虑使用spring提供的FactoryBean来动态的为每个数据源创建RedisTemplate和RedisHelper。public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
Set<String> names = loadRedisDataSourceName();
if (names.size() <= 0) {
logger.error("no multi datasource config, inject multi datasource failed. please check config.");
return;
}
logger.info("register redis datasource: {}", names);
// 为每个redis数据源注入BeanDefinition
for (String name : names) {
// 注册 RedisTemplate BeanDefinition
registerRedisTemplateBeanDefinition(name, RedisTemplateFactoryBean.class, registry);
// 注册 RedisHelper BeanDefinition
registerRedisHelperBeanDefinition(name, RedisHelperFactoryBean.class, registry);
}
}
==其他使用:见代码模块中的测试模块中的测试类!!!==
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。