1. Introduction
1.介绍
In this tutorial, we’re going to learn how to configure and implement Redis operations using Spring Data’s ReactiveRedisTemplate.
在本教程中,我们将学习如何使用Spring Data的ReactiveRedisTemplate配置和实现Redis操作。
We’ll go over the basic usages of the ReactiveRedisTemplate like how to store and retrieve objects in Redis. And we’ll take a look at how to execute Redis commands using the ReactiveRedisConnection.
我们将讨论ReactiveRedisTemplate的基本使用方法,比如如何在Redis中存储和检索对象。我们还将看看如何使用ReactiveRedisConnection执行Redis命令。
To cover the basics, check out our Introduction to Spring Data Redis.
要了解基础知识,请查看我们的Spring Data Redis 介绍。
2. Setup
2.设置
To use ReactiveRedisTemplate in our code, first, we need to add the dependency for Spring Boot’s Redis Reactive module:
要在我们的代码中使用ReactiveRedisTemplate,首先,我们需要添加Spring Boot的Redis Reactive模块的依赖项。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
3. Configuration
3.配置
Then, we need to establish a connection with our Redis server. We do not need to add any code for configuration if want to connect to a Redis server at localhost:6379.
然后,我们需要与我们的 Redis 服务器建立连接。如果想连接到位于localhost:6379的Redis服务器,我们不需要添加任何代码进行配置。
But, if our server were remote or were on a different port, we could supply the hostname and port in the LettuceConnectionFactory constructor:
但是,如果我们的服务器是远程的或在不同的端口上,我们可以在LettuceConnectionFactory 构造器中提供主机名和端口:。
@Bean
public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}
4. List Operations
4.列表操作
Redis Lists are lists of Strings sorted by insertion order. We can add or remove the elements from the List by pushing or popping them from the left or right.
Redis 列表是按插入顺序排序的字符串列表。我们可以通过从左边或右边推送或弹出元素来增加或删除列表中的元素。
4.1. String Template
4.1.字符串模板
To work with Lists, we’ll need an instance of ReactiveStringRedisTemplate to obtain a reference to RedisListOperations:
为了处理Lists,我们需要一个ReactiveStringRedisTemplate的实例来获得对RedisListOperations的引用。
@Autowired
private ReactiveStringRedisTemplate redisTemplate;
private ReactiveListOperations<String, String> reactiveListOps;
@Before
public void setup() {
reactiveListOps = redisTemplate.opsForList();
}
4.2. LPUSH and LPOP
4.2.LPUSH和LPOP
Now that we have an instance of ReactiveListOperations, let’s do an LPUSH operation for a list with demo_list as the list’s identifier.
现在我们有了一个ReactiveListOperations的实例,让我们为一个列表做一个LPUSH操作,demo_list作为列表的标识。
After that, we’ll do an LPOP on the list and then verify the element popped:
之后,我们将对列表做一个LPOP,然后验证弹出的元素。
@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();
}
Note that when testing reactive components, we can use StepVerifier to block for the completion of the task.
注意,在测试反应式组件时,我们可以使用StepVerifier来阻止任务的完成。
5. Value Operations
5.价值运作
We may want to use custom objects as well, and not just Strings.
我们可能也想使用自定义对象,而不仅仅是字符串。
So, let’s do some similar operations on an Employee object to demonstrate our operations on a POJO:
所以,让我们在一个Employee对象上做一些类似的操作,来演示我们对POJO的操作。
public class Employee implements Serializable {
private String id;
private String name;
private String department;
// ... getters and setters
// ... hashCode and equals
}
5.1. Employee Template
5.1.雇员模板
We’ll need to create a second instance of ReactiveRedisTemplate. We’ll still use String for our key, but this time the value will be Employee:
我们将需要创建第二个ReactiveRedisTemplate的实例。我们仍将使用String作为我们的键,但这次的值将是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);
}
In order to correctly serialize a custom object, we need to instruct Spring on how to do it. Here, we told the template to use the Jackson library by configuring a Jackson2JsonRedisSerializer for the value. Since the key is just a String, we can use the StringRedisSerializer for that.
为了正确地序列化一个自定义对象,我们需要指示Spring如何去做。在这里,我们告诉模板通过配置一个Jackson2JsonRedisSerializer来使用Jackson库来处理值。由于键只是一个字符串,我们可以使用StringRedisSerializer来处理。
We then take this serialization context and our connection factory to create a template as before.
然后我们利用这个序列化上下文和我们的连接工厂,像以前一样创建一个模板。
Next, we’ll create an instance of ReactiveValueOperations just like we did earlier with ReactiveListOperations:
接下来,我们将创建一个ReactiveValueOperations的实例,就像我们之前对ReactiveListOperations所做的那样。
@Autowired
private ReactiveRedisTemplate<String, Employee> redisTemplate;
private ReactiveValueOperations<String, Employee> reactiveValueOps;
@Before
public void setup() {
reactiveValueOps = redisTemplate.opsForValue();
}
5.2. Save and Retrieve Operations
5.2.保存和检索操作
Now that we have an instance of ReactiveValueOperations, let’s use it to store an instance of Employee:
现在我们有一个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();
}
And then we can get the same object back from Redis:
然后我们可以从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. Operations With Expiry Time
5.3.有过期时间的操作
We often want to put values in a cache that will naturally expire, and we can do this with the same set operation:
我们经常想把值放在一个自然会过期的缓存中,我们可以用同样的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();
}
Note that this test does some of its own blocking to wait for the cache key to expire.
请注意,这个测试做了一些自己的阻塞,以等待缓存密钥过期。
6. Redis Commands
6.Redis命令
Redis Commands are basically methods that a Redis client can invoke on a server. And Redis supports dozens of commands, some of which we have already seen, like LPUSH and LPOP.
Redis命令基本上是Redis客户端可以在服务器上调用的方法。Redis支持几十种命令,其中一些我们已经看到了,比如LPUSH和LPOP。
The Operations API is a higher-level abstraction around Redis’s set of commands.
操作API是围绕Redis的一系列命令的更高层次的抽象。
However, if we want to use the Redis command primitives more directly, then Spring Data Redis Reactive also gives us a Commands API.
然而,如果我们想更直接地使用 Redis 命令原语,那么 Spring Data Redis Reactive 也为我们提供了一个Commands API。
So, let’s take a look at the String and Key commands through the lens of the Commands API.
因此,让我们通过Commands API的视角来看看String和Key命令。
6.1. String and Key Commands
6.1.字符串和键命令
To perform Redis command operations we’ll obtain instances of ReactiveKeyCommands and ReactiveStringCommands.
为了执行Redis命令操作,我们将获得ReactiveKeyCommands和ReactiveStringCommands的实例。
We can get them both from our ReactiveRedisConnectionFactory instance:
我们可以从我们的ReactiveRedisConnectionFactory实例中获得它们。
@Bean
public ReactiveKeyCommands keyCommands(ReactiveRedisConnectionFactory
reactiveRedisConnectionFactory) {
return reactiveRedisConnectionFactory.getReactiveConnection().keyCommands();
}
@Bean
public ReactiveStringCommands stringCommands(ReactiveRedisConnectionFactory
reactiveRedisConnectionFactory) {
return reactiveRedisConnectionFactory.getReactiveConnection().stringCommands();
}
6.2. Set and Get Operations
6.2.设置和获取操作
We can use ReactiveStringCommands to store multiple keys with a single invocation, basically invoking the SET command multiple times.
我们可以使用ReactiveStringCommands,通过一次调用来存储多个键,基本上是多次调用SET命令。
And then, we can retrieve those keys through ReactiveKeyCommands, invoking the KEYS command:
然后,我们可以通过ReactiveKeyCommands,调用KEYS命令来检索这些键。
@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();
}
Note that, as stated earlier, this API is much more low-level. For example, instead of dealing with high-level objects, we are sending a stream of bytes, using ByteBuffer. Also, we use more of the Redis primitives like SET and SCAN.
请注意,如前所述,这个API是更低级的。例如,我们使用ByteBuffer发送一个字节流,而不是处理高级对象。此外,我们还使用了更多的Redis原语,如SET和SCAN。
Finally, String and Key Commands are just two among many command interfaces that Spring Data Redis exposes reactively.
最后,String 和 Key 命令只是 Spring Data Redis 反应式公开的众多命令接口中的两个。
7. Conclusion
7.结论
In this tutorial, we’ve covered the basics of using Spring Data’s Reactive Redis Template and the various ways in which we can integrate it with our application.
在本教程中,我们已经介绍了使用Spring Data的Reactive Redis模板的基本知识,以及将其与我们的应用程序整合的各种方法。
The full source code for the examples is available over on GitHub.
例子的完整源代码可在GitHub上获得over。