该版本仍在开发中,尚未被视为稳定。最新稳定版请使用Spring Session 3.5.3spring-doc.cadn.net.cn

响应式 Redis 索引配置

要开始使用 Redis 索引网页会话支持,你需要在项目中添加以下依赖:spring-doc.cadn.net.cn

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
implementation 'org.springframework.session:spring-session-data-redis'

然后加上@EnableRedisIndexedWebSession配置类注释:spring-doc.cadn.net.cn

@Configuration
@EnableRedisIndexedWebSession
public class SessionConfig {
    // ...
}

就是这样。您的应用程序现在拥有了Redis支持的被动索引网页会话支持。 既然你的应用已经配置好,你可能想开始自定义:spring-doc.cadn.net.cn

使用 JSON 序列化会话

默认情况下,Spring Session Data Redis 使用 Java 序列化来序列化会话属性。 有时候可能会有问题,尤其是当你有多个应用程序使用同一个 Redis 实例,但同一类有不同版本时。 你可以提供重写序列器Bean 可以自定义会话如何序列化到 Redis。 Spring Data Redis提供了GenericJackson2JsonRedisSerializer利用 Jackson 的对象映射器.spring-doc.cadn.net.cn

配置 RedisSerializer
@Configuration
public class SessionConfig implements BeanClassLoaderAware {

	private ClassLoader loader;

	/**
	 * Note that the bean name for this bean is intentionally
	 * {@code springSessionDefaultRedisSerializer}. It must be named this way to override
	 * the default {@link RedisSerializer} used by Spring Session.
	 */
	@Bean
	public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
		return new GenericJackson2JsonRedisSerializer(objectMapper());
	}

	/**
	 * Customized {@link ObjectMapper} to add mix-in for class that doesn't have default
	 * constructors
	 * @return the {@link ObjectMapper} to use
	 */
	private ObjectMapper objectMapper() {
		ObjectMapper mapper = new ObjectMapper();
		mapper.registerModules(SecurityJackson2Modules.getModules(this.loader));
		return mapper;
	}

	/*
	 * @see
	 * org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang
	 * .ClassLoader)
	 */
	@Override
	public void setBeanClassLoader(ClassLoader classLoader) {
		this.loader = classLoader;
	}

}

上面的代码片段使用了 Spring Security,因此我们创建了一个自定义代码对象映射器该模块使用了Spring Security的Jackson模块。 如果你不需要Spring Security Jackson模块,可以注入你的应用程序对象映射器然后像这样用:spring-doc.cadn.net.cn

@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer(ObjectMapper objectMapper) {
    return new GenericJackson2JsonRedisSerializer(objectMapper);
}

重写序列器豆子的名字必须是springSessionDefaultRedisSerializer这样它就不会和其他人冲突重写序列器Spring Data Redis使用的豆子。 如果提供不同名称,春季课程不会被采纳。spring-doc.cadn.net.cn

指定不同的命名空间

多个应用程序使用同一个 Redis 实例,或者希望将会话数据与存储在 Redis 中的其他数据分开,这种情况并不罕见。 因此,春季课程使用了Namespace(默认为春季:会期)以便在需要时保持会话数据分离。spring-doc.cadn.net.cn

你可以指定Namespace通过设置redisNamespace财产在@EnableRedisIndexedWebSession注解:spring-doc.cadn.net.cn

指定不同的命名空间
@Configuration
@EnableRedisIndexedWebSession(redisNamespace = "spring:session:myapplication")
public class SessionConfig {
    // ...
}

理解春季课程如何清理已过期的课程

Spring Session依赖Redis Keyspace Events来清理过期会话。 更具体地说,它监听发送到的事件__keyevent@*__:已过期__keyevent@*__:del通道并根据被销毁的密钥解析会话ID。spring-doc.cadn.net.cn

举个例子,假设我们有一个带有 id 的会话1234会议将在30分钟后结束。 当到期时间到达时,Redis会向__keyevent@*__:已过期传递信息的频道春季:会期:会期:到期:1234那把钥匙过期了。 春季会话随后会解析会话ID(1234从键中删除所有相关的会话键。spring-doc.cadn.net.cn

仅依赖 Redis 过期的一个问题是,如果密钥未被访问,Redis 无法保证过期事件何时会被触发。 更多详情请参见Redis文档中的“Redis密钥过期”。 为了避免事件过期不一定发生,我们可以确保每个密钥在预期到期时被访问。 这意味着如果密钥的TTL已过期,Redis会移除密钥并在尝试访问密钥时触发过期事件。 因此,每个会话的到期时间也会通过将会话ID存储在按到期时间排序的排序集中来跟踪。 这允许后台任务访问可能已过期的会话,确保 Redis 过期事件以更确定性的方式触发。 例如:spring-doc.cadn.net.cn

ZADD spring:session:sessions:expirations "1.702402961162E12" "648377f7-c76f-4f45-b847-c0268bb48381"

我们不会显式删除密钥,因为在某些情况下可能存在竞态条件,错误地将密钥标记为过期,而实际上并非如此。 除非使用分布式锁(这会毁掉我们的性能),否则无法确保到期映射的一致性。 通过访问密钥,我们确保只有在该密钥的TTL过期时才会移除密钥。spring-doc.cadn.net.cn

默认情况下,春季会话每60秒会检索最多100个过期会话。 如果您想配置清理任务的运行频率,请参阅“更改会话清理频率”部分。spring-doc.cadn.net.cn

配置 Redis 以发送密钥空间事件

默认情况下,Spring Session 尝试配置 Redis 发送密钥空间事件ConfigureNotifyKeyspaceEventsReactiveAction这反过来,可能会notify-keyspace-events(通知键空间事件)配置性质为EGX. 然而,如果 Redis 实例被妥善保护,这种策略将无法奏效。 在这种情况下,Redis实例应外部配置,并设置一个类型的BeanConfigureReactiveRedisAction.NO_OP应该暴露出来,以禁用自动配置。spring-doc.cadn.net.cn

@Bean
public ConfigureReactiveRedisAction configureReactiveRedisAction() {
    return ConfigureReactiveRedisAction.NO_OP;
}

更改会话清理频率

根据你应用的需求,你可能需要调整会话清理的频率。 为此,你可以暴露一个ReactiveSessionRepositoryCustomizer<ReactiveRedisIndexedSessionRepository>豆子并设置清理时间财产:spring-doc.cadn.net.cn

@Bean
public ReactiveSessionRepositoryCustomizer<ReactiveRedisIndexedSessionRepository> reactiveSessionRepositoryCustomizer() {
    return (sessionRepository) -> sessionRepository.setCleanupInterval(Duration.ofSeconds(30));
}

你也可以设置调用disableCleanupTask()以关闭清理任务。spring-doc.cadn.net.cn

@Bean
public ReactiveSessionRepositoryCustomizer<ReactiveRedisIndexedSessionRepository> reactiveSessionRepositoryCustomizer() {
    return (sessionRepository) -> sessionRepository.disableCleanupTask();
}

接管清理任务

有时,默认的清理任务可能不足以满足你的应用程序需求。 你可能需要采用不同的策略来清理过期的会话。 既然你知道会话 ID 存储在 键下的排序集合中春季:会期:场次:过期期并按保质时间排序你可以禁用默认清理任务,并提供你自己的策略。 例如:spring-doc.cadn.net.cn

@Component
public class SessionEvicter {

    private ReactiveRedisOperations<String, String> redisOperations;

    @Scheduled
    public Mono<Void> cleanup() {
        Instant now = Instant.now();
        Instant oneMinuteAgo = now.minus(Duration.ofMinutes(1));
        Range<Double> range = Range.closed((double) oneMinuteAgo.toEpochMilli(), (double) now.toEpochMilli());
        Limit limit = Limit.limit().count(1000);
        return this.redisOperations.opsForZSet().reverseRangeByScore("spring:session:sessions:expirations", range, limit)
                // do something with the session ids
                .then();
    }

}

聆听会议活动

很多时候,对会话事件做出反应是有价值的,比如你可能想根据会话生命周期进行某种处理。spring-doc.cadn.net.cn

你配置你的应用来监听SessionCreatedEvent,SessionDeletedEventSessionExpiredEvent事件。 在春季中有几种方式可以监听应用事件,在这个例子中,我们将使用@EventListener注解。spring-doc.cadn.net.cn

@Component
public class SessionEventListener {

    @EventListener
    public Mono<Void> processSessionCreatedEvent(SessionCreatedEvent event) {
        // do the necessary work
    }

    @EventListener
    public Mono<Void> processSessionDeletedEvent(SessionDeletedEvent event) {
        // do the necessary work
    }

    @EventListener
    public Mono<Void> processSessionExpiredEvent(SessionExpiredEvent event) {
        // do the necessary work
    }

}