1. 简介

在本教程中,我们将学习如何使用 Spring Data 提供的 ReactiveRedisTemplate 来配置并实现 Redis 的基本操作。

我们会介绍 ReactiveRedisTemplate 的常见用法,比如如何在 Redis 中存储和读取对象,并且会讲解如何通过 ReactiveRedisConnection 执行 Redis 命令。

如果你还不太熟悉 Redis 的基础操作,可以先阅读我们之前的 Spring Data Redis 入门指南

2. 依赖配置

要使用 ReactiveRedisTemplate,首先需要添加 Spring Boot 的 Redis 响应式模块依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

3. 连接配置

接着,我们需要建立与 Redis 服务器的连接。如果你的 Redis 服务器运行在 localhost:6379,那就不需要额外配置。

但如果 Redis 是远程部署的,或者使用了非默认端口,可以通过 LettuceConnectionFactory 来指定主机名和端口号:

@Bean
public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() {
    return new LettuceConnectionFactory(host, port);
}

4. 列表操作(List Operations)

Redis 的 List 是按插入顺序排序的字符串列表,支持从左边或右边进行插入(push)和弹出(pop)操作。

4.1. String 模板配置

要操作 List 类型的数据,我们需要使用 ReactiveStringRedisTemplate 来获取 RedisListOperations 实例:

@Autowired
private ReactiveStringRedisTemplate redisTemplate;
private ReactiveListOperations<String, String> reactiveListOps;
@Before
public void setup() {
    reactiveListOps = redisTemplate.opsForList();
}

4.2. LPUSH 和 LPOP 操作

有了 ReactiveListOperations 实例后,我们可以通过 leftPushAll 方法将多个元素从左边推入名为 demo_list 的列表中:

@Test
public void givenListAndValues_whenLeftPushAndLeftPop_thenLeftPushAndLeftPop() {
    Mono<Long> lPush = reactiveListOps.leftPushAll(LIST_NAME, "first", "second")
      .log("Pushed");
    StepVerifier.create(lPush)
      .expectNext(2L)
      .verifyComplete();
    Mono<String> lPop = reactiveListOps.leftPop(LIST_NAME)
      .log("Popped");
    StepVerifier.create(lPop)
      .expectNext("second")
      .verifyComplete();
}

⚠️ 注意:在测试响应式组件时,可以使用 StepVerifier 来等待操作完成。

5. 值操作(Value Operations)

除了字符串,我们也可以存储自定义对象。下面以 Employee 类为例,演示如何在 Redis 中存储和读取 POJO:

public class Employee implements Serializable {
    private String id;
    private String name;
    private String department;
    // ... getters and setters
    // ... hashCode and equals
}

5.1. 自定义模板配置

为了支持 Employee 对象的序列化,我们需要创建一个 ReactiveRedisTemplate 实例,key 使用字符串,value 使用 Employee 类型:

@Bean
public ReactiveRedisTemplate<String, Employee> reactiveRedisTemplate(
  ReactiveRedisConnectionFactory factory) {
    StringRedisSerializer keySerializer = new StringRedisSerializer();
    Jackson2JsonRedisSerializer<Employee> valueSerializer =
      new Jackson2JsonRedisSerializer<>(Employee.class);
    RedisSerializationContext.RedisSerializationContextBuilder<String, Employee> builder =
      RedisSerializationContext.newSerializationContext(keySerializer);
    RedisSerializationContext<String, Employee> context = 
      builder.value(valueSerializer).build();
    return new ReactiveRedisTemplate<>(factory, context);
}

我们使用 Jackson 库对 Employee 对象进行 JSON 序列化,key 使用 StringRedisSerializer 处理。

然后,创建 ReactiveValueOperations 实例:

@Autowired
private ReactiveRedisTemplate<String, Employee> redisTemplate;
private ReactiveValueOperations<String, Employee> reactiveValueOps;
@Before
public void setup() {
    reactiveValueOps = redisTemplate.opsForValue();
}

5.2. 存储与读取操作

使用 ReactiveValueOperations 存储一个 Employee 实例:

@Test
public void givenEmployee_whenSet_thenSet() {
    Mono<Boolean> result = reactiveValueOps.set("123", 
      new Employee("123", "Bill", "Accounts"));
    StepVerifier.create(result)
      .expectNext(true)
      .verifyComplete();
}

再从 Redis 中取出这个对象:

@Test
public void givenEmployeeId_whenGet_thenReturnsEmployee() {
    Mono<Employee> fetchedEmployee = reactiveValueOps.get("123");
    StepVerifier.create(fetchedEmployee)
      .expectNext(new Employee("123", "Bill", "Accounts"))
      .verifyComplete();
}

5.3. 设置过期时间

我们经常需要为缓存数据设置过期时间,这可以通过 set 方法的重载版本来实现:

@Test
public void givenEmployee_whenSetWithExpiry_thenSetsWithExpiryTime() 
  throws InterruptedException {
    Mono<Boolean> result = reactiveValueOps.set("129", 
      new Employee("129", "John", "Programming"), 
      Duration.ofSeconds(1));
    StepVerifier.create(result)
      .expectNext(true)
      .verifyComplete();
    Thread.sleep(2000L); 
    Mono<Employee> fetchedEmployee = reactiveValueOps.get("129");
    StepVerifier.create(fetchedEmployee)
      .expectNextCount(0L)
      .verifyComplete();
}

⚠️ 这个测试通过 Thread.sleep 等待键过期,模拟缓存失效的场景。

6. Redis 命令接口

Redis 命令是指客户端可以向 Redis 服务器发送的指令,例如 LPUSH、LPOP 等。

Spring Data Redis 提供了两种方式操作 Redis:

  • Operations API:更高层次的抽象,封装了常用操作。
  • Commands API:更底层的命令接口,允许直接调用 Redis 原始命令。

6.1. 获取命令接口

通过 ReactiveRedisConnectionFactory 获取 ReactiveKeyCommandsReactiveStringCommands 实例:

@Bean
public ReactiveKeyCommands keyCommands(ReactiveRedisConnectionFactory 
  reactiveRedisConnectionFactory) {
    return reactiveRedisConnectionFactory.getReactiveConnection().keyCommands();
}
@Bean
public ReactiveStringCommands stringCommands(ReactiveRedisConnectionFactory 
  reactiveRedisConnectionFactory) {
    return reactiveRedisConnectionFactory.getReactiveConnection().stringCommands();
}

6.2. SET 与 GET 命令示例

使用 ReactiveStringCommands 可以批量执行 SET 命令,然后通过 ReactiveKeyCommands 获取匹配的 key:

@Test
public void givenFluxOfKeys_whenPerformOperations_thenPerformOperations() {
    Flux<SetCommand> keys = Flux.just("key1", "key2", "key3", "key4");
      .map(String::getBytes)
      .map(ByteBuffer::wrap)
      .map(key -> SetCommand.set(key).value(key));
    StepVerifier.create(stringCommands.set(keys))
      .expectNextCount(4L)
      .verifyComplete();
    Mono<Long> keyCount = keyCommands.keys(ByteBuffer.wrap("key*".getBytes()))
      .flatMapMany(Flux::fromIterable)
      .count();
    StepVerifier.create(keyCount)
      .expectNext(4L)
      .verifyComplete();
}

⚠️ 注意:Commands API 是低层接口,使用的是字节流(ByteBuffer)而不是对象,更贴近 Redis 原生行为。

7. 总结

本教程介绍了 Spring Data Redis 的响应式模板 ReactiveRedisTemplate 的基本使用方法,包括列表操作、对象存储、过期时间设置以及底层命令接口的使用。

完整的示例代码可以在 GitHub 上获取。


原始标题:An Introduction to Spring Data Redis Reactive