Spring Session Data MongoDB Reactive未清理过期会话问题咨询
Spring Session Data MongoDB 过期会话未自动清理问题
我正尝试在Webflux Spring Cloud Gateway中将spring-session-data-redis替换为spring-session-data-mongo。此前使用Redis实现时,会话信息会在过期后自动删除,但使用响应式MongoDB实现时,旧会话数据会永久留存。
使用的技术栈:
- Spring Boot 3.1.5
- Webflux
- 响应式MongoDB
- Spring Security OAuth2流程
疑问:过期会话是否应被自动清理?数据库自动创建时,未看到会话集合上有相关日期索引。
POM依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId> <version>3.1.5</version> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-mongodb</artifactId> <version>3.1.3</version> </dependency>
预期与实际行为
- 预期行为:过期会话应从MongoDB中删除;
- 实际行为:旧会话留存于MongoDB;
相关版本
Reactor Netty 1.1.12、JVM 20
解决方案
核心结论
Spring Session Data MongoDB不会自动清理过期会话——这和Redis的机制不同:Redis本身内置了过期键自动淘汰功能,而MongoDB没有这种原生支持,需要手动配置TTL索引或定时任务来实现过期会话清理。
方案一:添加TTL索引(推荐)
TTL索引是MongoDB官方提供的自动清理过期文档的方式,只需针对会话集合的lastAccessedTime字段创建索引,MongoDB会自动删除超过指定时间未更新的文档。
方式1:通过代码自动创建索引
在项目中添加配置类,启动时自动为会话集合创建TTL索引:
@Configuration public class MongoSessionIndexConfig { private final ReactiveMongoTemplate reactiveMongoTemplate; private final SessionProperties sessionProperties; public MongoSessionIndexConfig(ReactiveMongoTemplate reactiveMongoTemplate, SessionProperties sessionProperties) { this.reactiveMongoTemplate = reactiveMongoTemplate; this.sessionProperties = sessionProperties; } @PostConstruct public void createSessionTTLIndex() { // 转换会话超时时间为秒 long expireSeconds = sessionProperties.getTimeout().getSeconds(); // 针对lastAccessedTime字段创建TTL索引 Index ttlIndex = Index.indexOn("lastAccessedTime", Sort.Direction.ASC) .expire(expireSeconds, TimeUnit.SECONDS); // 默认会话集合名为"sessions",也可通过spring.session.mongodb.collection配置修改 reactiveMongoTemplate.indexOps("sessions") .ensureIndex(ttlIndex) .subscribe(); } }
方式2:手动在MongoDB客户端创建索引
直接连接MongoDB执行以下命令(替换1800为你的会话超时时间,单位秒):
db.sessions.createIndex( { "lastAccessedTime": 1 }, { expireAfterSeconds: 1800 } )
方案二:定时任务清理过期会话
如果不想依赖TTL索引,可自定义定时任务定期清理过期会话:
- 开启定时任务支持,在启动类或配置类上添加
@EnableScheduling; - 创建清理服务:
@Service public class ExpiredSessionCleanupService { private final ReactiveMongoTemplate reactiveMongoTemplate; private final SessionProperties sessionProperties; public ExpiredSessionCleanupService(ReactiveMongoTemplate reactiveMongoTemplate, SessionProperties sessionProperties) { this.reactiveMongoTemplate = reactiveMongoTemplate; this.sessionProperties = sessionProperties; } @Scheduled(fixedRate = 3600000) // 每小时执行一次,可根据需求调整 public void cleanExpiredSessions() { // 计算过期时间:当前时间减去会话超时时间 Instant expireThreshold = Instant.now().minus(sessionProperties.getTimeout()); // 删除lastAccessedTime早于过期时间的会话 reactiveMongoTemplate.remove( Query.query(Criteria.where("lastAccessedTime").lt(expireThreshold)), "sessions" ) .subscribe(deleteResult -> System.out.println("清理过期会话数量:" + deleteResult.getDeletedCount()) ); } }
补充说明
- Spring Session Data MongoDB存储会话时,会自动记录
lastAccessedTime(最后访问时间)字段,TTL索引或定时任务都是基于这个字段判断会话是否过期; - 会话超时时间可通过
spring.session.timeout配置,默认是30分钟。
内容的提问来源于stack exchange,提问作者zargarf
相关产品推荐
相关产品推荐

