SpringBoot中使用Redis-Lettuce
- 配置pom
- 在`application.properties`配置`Redis`参数
- 协议参数设置
- 序列化参数设置
- 实现工具`Redis`操作工具类
- 单条数据测试
- 批量测试
在
SpringBoot
中一般直接引用spring-boot-starter-data-redis
这个starter来使用Redis
,其中具体实现方式有两种方式
1、Lettuce
2、Jedis
配置pom
注意,这里我们使用
lettuce
,需要用到连接池信息,这里使用的是apache
的commons-pool2
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.6.8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在application.properties
配置Redis
参数
具体参数信息参考
org.springframework.boot.autoconfigure.data.redis.RedisProperties
,不同版本有些微差别
spring.application.name=demo
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
spring.data.redis.password=XXXX
spring.data.redis.clientType=LETTUCE
spring.data.redis.lettuce.pool.enabled=true
spring.data.redis.lettuce.pool.min-idle=2
spring.data.redis.lettuce.pool.max-idle=20
spring.data.redis.lettuce.pool.max-active=20
spring.data.redis.lettuce.pool.max-wait=5000
协议参数设置
这里可以设置一些个性化的参数,其中一个就是协议版本,如果使用的
Redis
版本低于6.0,需要使用ProtocolVersion.RESP2
@Configuration
public class CustomLettuceClientConfigurationBuilderCustomizer implements LettuceClientConfigurationBuilderCustomizer {
private static final int TIMEOUT = 30;
private static final SocketOptions socketOptions = SocketOptions.builder()
.keepAlive(SocketOptions.KeepAliveOptions.builder().enable().idle(Duration.ofSeconds(TIMEOUT)).interval(Duration.ofSeconds(TIMEOUT / 3)).count(3).build())
.tcpUserTimeout(SocketOptions.TcpUserTimeoutOptions.builder().enable().tcpUserTimeout(Duration.ofSeconds(TIMEOUT)).build())
.build();
@Override
public void customize(LettuceClientConfiguration.LettuceClientConfigurationBuilder clientConfigurationBuilder) {
// redis 6.0以前版本需要使用ProtocolVersion.RESP2
clientConfigurationBuilder.clientOptions(ClientOptions.builder()
.socketOptions(socketOptions)
.protocolVersion(ProtocolVersion.RESP3)
.build());
}
}
序列化参数设置
1、这里直接使用的
StringRedisTemplate
,要求全部是字符串,序列化的工作交给调用方自己处理,
2、如果要扩展RedisTemplate<K, V>
,实现自己的特殊逻辑,这里就要设置key、value的序列化方式
@Configuration
public class RedisConfig {
@Bean
public StringRedisTemplate stringRedisTemplate(LettuceConnectionFactory factory) {
// 验证是否可用
factory.validateConnection();
// 其他参数
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(factory);
return template;
}
}
实现工具Redis
操作工具类
public interface RedisService {
/**
* 写入缓存
*
* @param key 缓存key
* @param value 缓存value
* @param timeout 超时时间
* @param timeUnit 时间单位
* @return
*/
void set(String key, String value, long timeout, TimeUnit timeUnit);
/**
* 获取缓存值
*
* @param key 缓存key
* @return
*/
String get(String key);
}
@Service
public class RedisServiceImpl implements RedisService {
@Autowired
private StringRedisTemplate redisTemplate;
@Override
public void set(String key, String value, long timeout, TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}
@Override
public String get(String key) {
return redisTemplate.opsForValue().get(key);
}
}
单条数据测试
@SpringBootTest
public class DemoApplicationTests {
@Autowired
private RedisService redisService;
@Test
public void testSetGet() {
String key = "hello";
String value = "测试数据";
redisService.set(key, value, 10, TimeUnit.SECONDS);
Assertions.assertEquals(value, redisService.get(key));
}
}
批量测试
这里主要测试一下连接池参数,找到一个合理配置
public class DemoApplicationTests {
@Autowired
private RedisService redisService;
@Test
public void testPool() {
this.testSetGet();
ExecutorService executorService = Executors.newFixedThreadPool(10);
int total = 10000;
for (int i = 0; i < 10; i++) {
int calcTotal = testPool(total, executorService);
Assertions.assertEquals(calcTotal, total);
}
executorService.shutdown();
executorService.close();
}
private int testPool(int total, ExecutorService executorService) {
long start = System.currentTimeMillis();
List<CompletableFuture<Integer>> list = IntStream.rangeClosed(1, total)
.mapToObj(it -> {
return build(it, executorService);
}).toList();
CompletableFuture.allOf(list.toArray(CompletableFuture[]::new)).join();
int calcTotal = list.stream().filter(Objects::nonNull)
.map(it -> {
try {
return it.get();
} catch (Exception e) {
return 0;
}
}).reduce(0, Integer::sum);
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) / 1000.0 + "s");
return calcTotal;
}
private CompletableFuture<Integer> build(int it, ExecutorService executorService) {
final String key = "test:hello:" + it;
final String value = "value-" + it;
return CompletableFuture.supplyAsync(() -> {
try {
redisService.set(key, value, 10, TimeUnit.SECONDS);
boolean suc = value.equals(redisService.get(key));
// System.out.println("build -> " + it + " -> " + Thread.currentThread().getName());
if (!suc) {
System.out.println("failed ->" + it);
return 0;
} else {
return 1;
}
} catch (Exception e) {
System.out.println("error ->" + it + ", " + e.getMessage());
return 0;
}
}, executorService);
}
}