如何避免Tomcat重载时RedissonSessionManager触发Redisson关闭
核心原因
RedissonSessionManager默认将客户端生命周期与Tomcat Web上下文绑定,当上下文因重载销毁时,会触发Redisson客户端的关闭钩子,导致客户端停止。要解决这个问题,核心是让Redisson客户端的生命周期脱离单个Web上下文的管控。
方案1:使用Tomcat全局共享Redisson客户端(官方推荐)
通过Tomcat的全局命名资源管理Redisson客户端,让所有Web上下文共享同一个实例,单个上下文重载不会影响全局客户端的运行。
步骤:
在Tomcat全局配置中定义Redisson客户端资源
编辑Tomcat的conf/server.xml,在<GlobalNamingResources>节点下添加如下配置:<GlobalNamingResources> <!-- 全局Redisson客户端资源 --> <Resource name="redisson/RedissonClient" auth="Container" type="org.redisson.api.RedissonClient" factory="org.redisson.tomcat.RedissonClientFactory" configPath="/absolute/path/to/your/redisson.yaml"/> </GlobalNamingResources>注意
configPath要填写Redisson配置文件的绝对路径。在Web应用中引用全局资源
编辑Web应用的META-INF/context.xml,添加资源链接:<Context> <!-- 引用全局Redisson客户端 --> <ResourceLink name="redisson/RedissonClient" global="redisson/RedissonClient" type="org.redisson.api.RedissonClient"/> <!-- 配置RedissonSessionManager使用共享客户端 --> <Manager className="org.redisson.tomcat.RedissonSessionManager" redissonClientJndiName="redisson/RedissonClient"/> </Context>这里不需要再指定
configPath,因为客户端已经由全局资源初始化完成。验证效果
启动Tomcat后,Redisson客户端会由Tomcat全局管理,当Web上下文重载时,仅会销毁当前应用的SessionManager实例,不会触发全局客户端的关闭逻辑。
方案2:禁用Redisson关闭钩子并手动管理生命周期
通过配置禁用Redisson自带的关闭钩子,同时在Tomcat全局级别手动初始化和关闭客户端,脱离Web上下文的生命周期管控。
步骤:
修改Redisson配置文件
在redisson.yaml中添加以下配置,关闭自动关闭钩子:# 禁用Redisson关闭钩子 shutdownHook: false # 启用引用功能,避免客户端被意外回收 referenceEnabled: true编写全局监听器管理客户端生命周期
创建一个Servlet上下文监听器,在Tomcat启动时初始化Redisson客户端,停止时手动关闭:import org.redisson.api.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.servlet.annotation.WebListener; @WebListener public class GlobalRedissonManager implements ServletContextListener { private static RedissonClient redissonClient; @Override public void contextInitialized(ServletContextEvent event) { // 加载Redisson配置并初始化客户端 Config config = Config.fromYAML("/absolute/path/to/redisson.yaml"); redissonClient = Redisson.create(config); // 将客户端实例存入全局上下文 event.getServletContext().setAttribute("globalRedissonClient", redissonClient); } @Override public void contextDestroyed(ServletContextEvent event) { // Tomcat停止时手动关闭客户端 if (redissonClient != null && !redissonClient.isShutdown()) { redissonClient.shutdown(); } } }配置RedissonSessionManager使用全局客户端
在context.xml的Manager标签中指定客户端实例:<Manager className="org.redisson.tomcat.RedissonSessionManager" redissonClientAttributeName="globalRedissonClient"/>
方案3:关闭Tomcat上下文自动重载(备选)
如果不需要频繁重载Web上下文,可以将context.xml中的reloadable属性设为false:
<Context reloadable="false"> <!-- 其他配置 --> </Context>
这种方式简单直接,但会失去热重载应用的能力,仅适合不需要频繁更新的场景。
最佳实践
- 优先选择方案1,这是Redisson官方针对Tomcat上下文重载场景提供的标准解决方案,既能保证客户端稳定运行,又能避免连接池资源浪费。
- 避免每个Web应用单独初始化Redisson客户端,多个客户端实例会占用更多Redis连接资源,且容易出现生命周期管理问题。
- 若使用方案2,必须确保在Tomcat停止时手动关闭Redisson客户端,否则会导致Redis连接泄漏。
内容的提问来源于stack exchange,提问作者Muhammad Osama

