|
@@ -0,0 +1,49 @@
|
|
|
|
|
+package com.fs.core.config;
|
|
|
|
|
+
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.redisson.api.RedissonClient;
|
|
|
|
|
+import org.springframework.web.context.WebApplicationContext;
|
|
|
|
|
+import org.springframework.web.context.support.WebApplicationContextUtils;
|
|
|
|
|
+
|
|
|
|
|
+import javax.servlet.ServletContextEvent;
|
|
|
|
|
+import javax.servlet.ServletContextListener;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Web 应用销毁时主动关闭 Redisson,避免 Tomcat 报错:
|
|
|
|
|
+ * "The web application [ROOT] appears to have started a thread named [redisson-netty-*] but has failed to stop it."
|
|
|
|
|
+ */
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+public class RedissonShutdownListener implements ServletContextListener {
|
|
|
|
|
+
|
|
|
|
|
+ /** 关闭后等待 Netty 线程退出的时间(毫秒) */
|
|
|
|
|
+ private static final int SHUTDOWN_WAIT_MS = 3000;
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void contextDestroyed(ServletContextEvent sce) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
|
|
|
|
|
+ if (ctx == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!ctx.containsBean("redissonClient")) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ RedissonClient client = ctx.getBean(RedissonClient.class);
|
|
|
|
|
+ if (client != null && !client.isShutdown()) {
|
|
|
|
|
+ log.info("[Redisson] 应用卸载,正在关闭 RedissonClient 以避免 Netty 线程泄漏");
|
|
|
|
|
+ client.shutdown();
|
|
|
|
|
+ Thread.sleep(SHUTDOWN_WAIT_MS);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (InterruptedException e) {
|
|
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
|
|
+ log.warn("[Redisson] 等待关闭时被中断", e);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("[Redisson] 关闭 RedissonClient 时异常", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void contextInitialized(ServletContextEvent sce) {
|
|
|
|
|
+ // no-op
|
|
|
|
|
+ }
|
|
|
|
|
+}
|