Spring Boot 使用 Spring Session Redis 后如何采集会话指标?
使用 Spring Session Redis 后,原来的六项 Tomcat 会话指标不能直接反映 Redis 中的会话状态。Spring Boot 的标准 Tomcat 指标采集没有提供自动切换到 Redis 会话统计的等价方案。如果需要这些统计维度,需要补充自定义采集逻辑,但仍可复用 Actuator、Micrometer 和现有监控系统。
为什么接入 Redis 后指标停止更新?
问题中列出的 tomcat_sessions_* 指标来自 Tomcat 自身的会话管理器。Micrometer 的 TomcatMetrics 读取 org.apache.catalina.Manager,例如通过 getActiveSessions() 获取当前会话数,通过 getSessionCounter() 获取创建总数。采集实现
Spring Session Redis 的自动配置生效后,SessionRepositoryFilter 会让应用使用由 Redis 支撑的 HttpSession 实现。会话生命周期由 Spring Session 管理,因此 Tomcat 管理器的统计不会随这些会话的创建和销毁而更新。Spring Session 工作原理
统计来源改变后,指标可能仍然存在,却保持为零或不再变化。如果指标完全消失,则需要检查 Actuator、指标过滤与 Tomcat 采集配置。仅凭添加依赖这一信息,无法确定指标消失的具体原因。
六项指标应如何替代?
先确定统计范围:是单个应用进程,还是同一 Redis 会话命名空间下的全部会话。共享 Redis 后,这两种统计结果可能相差很大。
| 原指标 | 自定义采集时的处理方式 |
|---|---|
tomcat_sessions_active_current_sessions | 统计当前仍有效的会话,使用 Gauge;不能简单等同于 Redis key 数量 |
tomcat_sessions_active_max_sessions | 记录当前有效会话数的历史峰值;它不是配置的会话数量上限 |
tomcat_sessions_alive_max_seconds | 按选定的结束条件记录会话存活时长,并明确最大值的保留周期 |
tomcat_sessions_created_sessions_total | 根据创建事件或首次成功持久化记录 Counter,并说明采用哪种口径 |
tomcat_sessions_expired_sessions_total | 根据超时事件记录 Counter,同时明确是否包含主动失效 |
tomcat_sessions_rejected_sessions_total | 在实际执行会话准入限制的位置计数 |
Tomcat 的 rejected 指因达到最大活动会话数而未能创建的会话。Redis 写入失败、认证失败或连接池耗尽都不能直接算作同一种拒绝。Tomcat Manager 定义
自定义时建议使用独立指标前缀,例如 app.session.*,避免监控面板继续把它们解释为 Tomcat 本地统计。
如何用会话事件接入 Micrometer?
如果只需要创建、超时和主动删除的观测计数,可以监听 Spring Session 事件,前提是实际使用的仓库支持发布这些事件。RedisSessionRepository 不支持会话事件发布,而 RedisIndexedSessionRepository 支持相关事件。仓库 API 说明
如果项目支持以下属性,且由 Spring Boot 自动配置会话仓库,可以设置:
spring.session.redis.repository-type=indexed
这项配置会选择支持索引和事件的仓库,但不会自动生成上述六项指标。原问题没有提供版本和显式配置,使用前应核对项目对应版本,并确认实际的 SessionRepository 类型。Redis 仓库配置
下面的示例假设应用已经配置好 Micrometer 的 MeterRegistry,并且能够收到相应的会话事件。它记录当前应用实例收到的事件次数:
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.context.event.EventListener;
import org.springframework.session.events.SessionCreatedEvent;
import org.springframework.session.events.SessionDeletedEvent;
import org.springframework.session.events.SessionExpiredEvent;
import org.springframework.stereotype.Component;
@Component
public class SessionEventMetrics {
private final Counter created;
private final Counter expired;
private final Counter deleted;
public SessionEventMetrics(MeterRegistry registry) {
this.created = registry.counter("app.session.events.created");
this.expired = registry.counter("app.session.events.expired");
this.deleted = registry.counter("app.session.events.deleted");
}
@EventListener
public void onCreated(SessionCreatedEvent event) {
created.increment();
}
@EventListener
public void onExpired(SessionExpiredEvent event) {
expired.increment();
}
@EventListener
public void onDeleted(SessionDeletedEvent event) {
deleted.increment();
}
}
示例将超时与主动删除分开统计。SessionExpiredEvent 和 SessionDeletedEvent 都属于 SessionDestroyedEvent;如果同时监听父类和子类来累加同一个销毁计数,会出现重复计算。事件关系说明
为什么不能直接用创建数减销毁数计算当前会话数?
这种算法需要从完整、可靠的初始状态开始,并且不能遗漏或重复处理事件。实际部署中容易遇到以下情况:
- 应用启动时,Redis 中已经存在会话,本地计数却从零开始。
- 多个实例订阅同一会话命名空间,可能收到同一批事件;直接求和会重复统计。
- 实例重启、订阅断开或事件处理失败,可能导致计数与实际状态偏离。
- 会话超时事件依赖 Redis Keyspace notifications,通知时间也不保证等于逻辑过期时间。
Spring Session 对 Redis Cluster 的事件订阅也有额外限制,不能默认每个节点上的事件都能被完整观测。事件与过期机制、Redis Cluster 注意事项
如果需要可信的全局当前会话数,应维护可校准的共享统计状态或专用会话索引,处理过期、主动失效和 Session ID 变更,并明确允许的统计延迟。可以定时更新采集结果,再由 Gauge 读取,避免每次监控抓取都遍历 Redis。
也不要直接统计整个命名空间的 key:其中可能包含会话数据、索引和过期辅助结构,物理 key 的存在时间也不一定等于会话有效时间。
如何确认采集结果符合预期?
先确认实际仓库类型,再分别验证新建会话、正常访问、主动失效和超时几个场景。正常访问已有会话时,不应不断增加创建计数;多实例部署时,应检查一次会话操作会被多少实例观测到,并据此确定聚合方式。
若还需要排查 Tomcat 指标本身是否启用,可按对应版本检查 server.tomcat.mbeanregistry.enabled=true。它只影响 Tomcat 采集,不会让 Tomcat 开始统计 Redis 会话。Spring Boot 指标配置
备注:内容仅供参考。
