You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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索引,可自定义定时任务定期清理过期会话:

  1. 开启定时任务支持,在启动类或配置类上添加@EnableScheduling;
  2. 创建清理服务:
@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 16:42:19