9.4.1 使用 Spring Session Data Redis 处理会话

Spring 通过 Spring Session 项目提供会话管理功能。默认情况下,会话数据存储在内存中,但这在云原生应用程序中是不可行的。你希望将其保存在外部服务中,以便数据在应用程序关闭后仍然保留。使用分布式会话存储的另一个基本原因是,你通常有给定应用程序的多个实例。你希望它们访问相同的会话数据,以便为用户提供无缝体验。

Redis 是会话管理的流行选择,并且受到 Spring Session Data Redis 的支持。此外,你已经为限流器设置了它。你可以通过最少的配置将其添加到 Edge Service。

首先,你需要在 Edge Service 项目的 build.gradle 文件中添加对 Spring Session Data Redis 的新依赖。你还可以添加 Testcontainers 库,以便在编写集成测试时使用轻量级 Redis 容器。记住在添加后刷新并重新导入 Gradle 依赖。

代码清单 9.16 添加 Spring Session 和 Testcontainers 依赖

ext {
 // ... 其他属性
 set('testcontainersVersion', "1.17.3")
}

dependencies {
 // ... 其他依赖
 implementation 'org.springframework.session:spring-session-data-redis'
 testImplementation 'org.testcontainers:junit-jupiter'
}

dependencyManagement {
 imports {
 // ... 其他导入
 mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}"
 }
}

接下来,你需要指示 Spring Boot 使用 Redis 进行会话管理(spring.session.store-type),并定义一个唯一命名空间来为来自 Edge Service 的所有会话数据添加前缀(spring.session.redis.namespace)。你还可以定义会话超时(spring.session.timeout)。如果不指定超时,默认为 30 分钟。

在 application.yml 文件中配置 Spring Session 如下。

代码清单 9.17 配置 Spring Session 将数据存储在 Redis 中

spring:
 session:
 store-type: redis
 timeout: 10m
 redis:
 namespace: polar:edge

在网关中管理 Web 会话需要一些额外的注意事项,以确保你在正确的时间保存正确的状态。在这个示例中,我们希望在将请求转发到下游之前将会话保存在 Redis 中。我们该怎么做?如果你在想是否有用于此的网关过滤器,你是对的!

在 Edge Service 项目的 application.yml 文件中,将 SaveSession 作为默认过滤器添加,以指示 Spring Cloud Gateway 在将请求转发到下游之前始终保存 Web 会话。

代码清单 9.18 配置网关保存会话数据

spring:
 cloud:
 gateway:
 default-filters:
 - SaveSession # 确保在将请求转发到下游之前保存会话数据

当 Spring Session 与 Spring Security 结合使用时,这是一个关键点。第 11 章和第 12 章将介绍有关会话管理的更多细节。现在,让我们设置一个集成测试来验证 Edge Service 中的 Spring 上下文是否正确加载,包括与 Redis 的集成。

我们将使用的方法与上一章中用于定义 PostgreSQL 测试容器的方法类似。让我们扩展 Spring Initializr 生成的现有 EdgeServiceApplicationTests 类,并配置一个 Redis 测试容器。对于这个示例,只需验证当 Redis 用于存储与 Web 会话相关的数据时,Spring 上下文是否正确加载即可。

代码清单 9.19 使用 Redis 容器测试 Spring 上下文加载

package com.polarbookshop.edgeservice;

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers // 激活测试容器的自动启动和清理
class EdgeServiceApplicationTests {

 private static final int REDIS_PORT = 6379;

 @Container
 static GenericContainer<?> redis =
 new GenericContainer<>(DockerImageName.parse("redis:7.0"))
 .withExposedPorts(REDIS_PORT); // 定义用于测试的 Redis 容器

 @DynamicPropertySource
 static void redisProperties(DynamicPropertyRegistry registry) {
 registry.add("spring.redis.host", () -> redis.getHost());
 registry.add("spring.redis.port", () -> redis.getMappedPort(REDIS_PORT));
 // 覆盖 Redis 配置以指向测试 Redis 实例
 }

 @Test
 void contextLoads() {
 // 验证 Spring 上下文加载
 }
}

这个测试验证了当 Edge Service 使用 Redis 进行会话管理时,Spring 上下文可以正确加载。测试容器会在测试开始时自动启动 Redis 容器,在测试结束后自动清理。

results matching ""

    No results matching ""