package com.example.demo;
public record Coffee(String id, String name) {
}
使用 Redis 响应式访问数据
本指南将引导你创建一个功能性的响应式应用程序,该应用程序使用 Spring Data 通过非阻塞的 Lettuce 驱动程序与 Redis 交互。
你将构建什么
你将构建一个 Spring 应用程序,它使用 Spring Data Redis 和 Project Reactor 以响应式方式与 Redis 数据存储交互,存储和检索 Coffee
对象而不会阻塞。此应用程序使用基于 Reactive Streams 规范的 Reactor Publisher
实现,即 Mono
(用于返回 0 或 1 个值的 Publisher)和 Flux
(用于返回 0 到 n 个值的 Publisher)。
你需要什么
-
大约 15 分钟
-
喜欢的文本编辑器或 IDE
-
Java 17 或更高版本
如何完成本指南
要在本地环境中查看最终结果,你可以执行以下任一操作:
-
下载并解压本指南的源仓库
-
使用 Git 克隆仓库:
git clone https://github.com/spring-guides/gs-spring-data-reactive-redis.git
-
Fork 该仓库,通过提交 pull request 来请求修改本指南
设置 Redis 服务器
在构建消息传递应用程序之前,你需要设置服务器来处理消息的接收和发送。本指南假定你使用 Spring Boot Docker Compose 支持。这种方法的一个前提是你的开发机器具备 Docker 环境,例如 Docker Desktop。添加一个执行以下操作的依赖项 spring-boot-docker-compose
-
在你的工作目录中搜索
compose.yml
文件和其他常见的 compose 文件名 -
使用发现的
compose.yml
调用docker compose up
-
为每个支持的容器创建服务连接 bean
-
应用程序关闭时调用
docker compose stop
要使用 Docker Compose 支持,你只需遵循本指南即可。根据你引入的依赖项,Spring Boot 会找到正确的 compose.yml
文件并在你运行应用程序时启动你的 Docker 容器。
如果你选择自己运行 Redis 服务器而不是使用 Spring Boot Docker Compose 支持,你有以下几种选择: - 下载服务器并手动运行 - 如果你使用 Mac,可以通过 Homebrew 安装 - 手动使用 docker compose up
命令运行 compose.yaml
文件
如果选择这些替代方法中的任何一种,你应该从 Maven 或 Gradle 构建文件中移除 spring-boot-docker-compose
依赖项。你还需要向 application.properties
文件添加配置,如准备构建应用程序部分中更详细的描述。如前所述,本指南假定你使用 Spring Boot 中的 Docker Compose 支持,因此此时无需对 application.properties
进行额外的更改。
从 Spring Initializr 开始
你可以使用这个 预配置项目并点击 Generate 下载 ZIP 文件。此项目已配置好以适合本教程中的示例。
手动初始化项目
-
导航到 https://start.spring.io。该服务会引入应用程序所需的所有依赖项并为你完成大部分设置。
-
选择 Gradle 或 Maven 以及你想使用的语言。本指南假定你选择了 Java。
-
点击 Dependencies 并选择 Spring Reactive Web、Spring Data Reactive Redis 和 Docker Compose Support。
-
点击 Generate。
-
下载生成的 ZIP 文件,它是一个包含你所选配置的 web 应用程序存档。
如果你的 IDE 集成了 Spring Initializr,你可以直接从 IDE 中完成此过程。 |
创建域类
创建一个 record,代表我们希望在咖啡目录中存入的一种咖啡
src/main/java/com/example/demo/Coffee.java
创建配置类
创建一个包含支持响应式 Redis 操作的 Spring Beans 的类
src/main/java/com/example/demo/CoffeeConfiguration.java
package com.example.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.core.ReactiveRedisOperations;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class CoffeeConfiguration {
@Bean
ReactiveRedisOperations<String, Coffee> redisOperations(ReactiveRedisConnectionFactory factory) {
Jackson2JsonRedisSerializer<Coffee> serializer = new Jackson2JsonRedisSerializer<>(Coffee.class);
RedisSerializationContext.RedisSerializationContextBuilder<String, Coffee> builder =
RedisSerializationContext.newSerializationContext(new StringRedisSerializer());
RedisSerializationContext<String, Coffee> context = builder.value(serializer).build();
return new ReactiveRedisTemplate<>(factory, context);
}
}
创建用于加载数据的 Spring Bean
创建一个 Spring Bean,在应用程序启动时加载示例数据
由于我们可能会多次(重新)启动应用程序,因此应首先删除之前执行可能仍然存在的任何数据。我们使用 flushAll() (Redis)服务器命令来完成此操作。清除所有现有数据后,我们创建一个小的 Flux ,将每个咖啡名称映射到一个 Coffee 对象,并将其保存到响应式 Redis 仓库中。然后我们查询仓库中的所有值并显示它们。 |
src/main/java/com/example/demo/CoffeeLoader.java
package com.example.demo;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.core.ReactiveRedisOperations;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import jakarta.annotation.PostConstruct;
import java.util.UUID;
@Component
public class CoffeeLoader {
private final ReactiveRedisConnectionFactory factory;
private final ReactiveRedisOperations<String, Coffee> coffeeOps;
public CoffeeLoader(ReactiveRedisConnectionFactory factory, ReactiveRedisOperations<String, Coffee> coffeeOps) {
this.factory = factory;
this.coffeeOps = coffeeOps;
}
@PostConstruct
public void loadData() {
factory.getReactiveConnection().serverCommands().flushAll().thenMany(
Flux.just("Jet Black Redis", "Darth Redis", "Black Alert Redis")
.map(name -> new Coffee(UUID.randomUUID().toString(), name))
.flatMap(coffee -> coffeeOps.opsForValue().set(coffee.id(), coffee)))
.thenMany(coffeeOps.keys("*")
.flatMap(coffeeOps.opsForValue()::get))
.subscribe(System.out::println);
}
}
创建 RestController
创建一个 RestController
为我们的应用程序提供外部接口
src/main/java/com/example/demo/CoffeeController.java
package com.example.demo;
import org.springframework.data.redis.core.ReactiveRedisOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class CoffeeController {
private final ReactiveRedisOperations<String, Coffee> coffeeOps;
CoffeeController(ReactiveRedisOperations<String, Coffee> coffeeOps) {
this.coffeeOps = coffeeOps;
}
@GetMapping("/coffees")
public Flux<Coffee> all() {
return coffeeOps.keys("*")
.flatMap(coffeeOps.opsForValue()::get);
}
}
运行应用程序
你可以通过 IDE 运行 main 方法。请注意,如果你是从解决方案仓库克隆的项目,你的 IDE 可能会在错误的位置查找 compose.yaml
文件。你可以配置你的 IDE 以在正确的位置查找,或者你可以使用命令行运行应用程序。./gradlew bootRun
和 ./mvnw spring-boot:run
命令将启动应用程序并自动查找 compose.yaml 文件。
测试应用程序
应用程序运行后,从新的终端运行以下命令
curl http://localhost:8080/coffees
你应该看到以下输出
[
{
"id": "04ce0843-c9f8-40f6-942f-1ff643c1d426",
"name": "Jet Black Redis"
},
{
"id": "e2a0d798-5fa4-48a2-a45c-7770d8bb82bf",
"name": "Black Alert Redis"
},
{
"id": "13f13e3a-0798-44b7-8ae4-b319b227bb19",
"name": "Darth Redis"
}
]
准备构建应用程序
要在没有 Spring Boot Docker Compose 支持的情况下运行代码,你需要一个本地运行的 Redis 版本进行连接。为此,你可以使用 Docker Compose,但必须首先对 compose.yaml
文件进行两项更改。首先,将 compose.yaml
中的 ports
条目修改为 '6379:6379'
。其次,添加一个 container_name
。
现在 compose.yaml
应该是
services: redis: container_name: 'guide-redis' image: 'redis:latest' ports: - '6379:6379'
现在你可以运行 docker compose up
来启动 Redis 服务器。现在你应该有一个准备好接受请求的外部 Redis 服务器。你可以重新运行应用程序并使用你的外部 Redis 服务器看到相同的输出。
application.properties 文件中无需配置,因为默认值与 compose.yaml 中的 Redis 服务器配置匹配。具体来说,属性 spring.data.redis.host 和 spring.data.redis.port 分别默认为 localhost 和 6379 。 |
构建应用程序
本节介绍运行本指南的不同方式
无论你选择哪种方式运行应用程序,输出应该都是相同的。
要运行应用程序,可以将其打包为可执行的 jar 文件。./mvnw clean package
命令会将应用程序编译为可执行 jar。然后可以使用 java -jar target/demo-0.0.1-SNAPSHOT.jar
命令运行该 jar 文件。
或者,如果你有可用的 Docker 环境,可以直接使用 buildpacks 从 Maven 或 Gradle 插件创建 Docker 镜像。使用 Cloud Native Buildpacks,你可以创建 Docker 兼容的镜像,这些镜像可以在任何地方运行。Spring Boot 直接为 Maven 和 Gradle 包含了 buildpack 支持。这意味着你只需输入一个命令,就可以快速将一个合理的镜像放入本地运行的 Docker daemon 中。要使用 Cloud Native Buildpacks 创建 Docker 镜像,运行 ./mvnw spring-boot:build-image
命令。启用 Docker 环境后,可以使用 docker run --network container:guide-redis docker.io/library/demo:0.0.1-SNAPSHOT
命令运行应用程序。
--network 标志告诉 Docker 将我们的指南容器附加到外部容器正在使用的现有网络。你可以在 Docker 文档中找到更多信息。 |
原生镜像支持
Spring Boot 也支持编译到原生镜像,前提是你的机器上安装了 GraalVM 分发版。
然后可以运行 ./mvnw -Pnative native:compile
命令生成原生镜像。构建完成后,通过执行 target/demo
命令,你将能够以近乎即时的启动时间运行代码。
要使用 Maven 创建原生镜像容器,你应该确保你的 pom.xml
文件使用了 spring-boot-starter-parent
和 org.graalvm.buildtools:native-maven-plugin
。此插件应位于 <build> <plugins>
部分中
<plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> </plugin>
你还可以使用 Buildpacks 创建原生镜像。通过运行 ./mvnw -Pnative spring-boot:build-image
命令可以生成原生镜像。构建完成后,可以使用 docker run --network container:guide-redis docker.io/library/demo:0.0.1-SNAPSHOT
命令启动应用程序。
在 Docker 中测试应用程序
如果你使用了 Docker 指令运行应用程序(如前面所示),简单的 curl 命令从终端或命令行将不再起作用。这是因为我们在一个Docker 网络中运行容器,该网络无法从终端或命令行访问。要运行 curl 命令,我们可以启动第三个容器来运行 curl 命令,并将其附加到同一网络。
首先,获取一个新容器的交互式 shell,该容器运行在与 Redis 容器和应用程序相同的网络中
docker run --rm --network container:guide-redis -it alpine
接下来,从容器内的 shell 中安装 curl
apk add curl
最后,你可以按照测试应用程序中描述的方式运行 curl 命令。
总结
恭喜!你已经开发了一个 Spring 应用程序,它使用 Spring Data 和 Redis 实现了完全响应式、非阻塞的数据库访问!
想写新的指南或为现有指南贡献力量?请查看我们的贡献指南。
所有指南的代码均以 ASLv2 许可发布,写作内容以 署名-禁止演绎知识共享许可 发布。 |