diff --git a/snowy-plugin/snowy-plugin-dev/src/main/java/vip/xiaonuo/dev/modular/message/websocket/DevMessageWebSocket.java b/snowy-plugin/snowy-plugin-dev/src/main/java/vip/xiaonuo/dev/modular/message/websocket/DevMessageWebSocket.java index 2e837fb4..ad58d5d3 100644 --- a/snowy-plugin/snowy-plugin-dev/src/main/java/vip/xiaonuo/dev/modular/message/websocket/DevMessageWebSocket.java +++ b/snowy-plugin/snowy-plugin-dev/src/main/java/vip/xiaonuo/dev/modular/message/websocket/DevMessageWebSocket.java @@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.http.HttpUtil; import cn.hutool.extra.spring.SpringUtil; import cn.hutool.json.JSONUtil; +import jakarta.annotation.PreDestroy; import jakarta.websocket.*; import jakarta.websocket.server.ServerEndpoint; import lombok.extern.slf4j.Slf4j; @@ -12,12 +13,14 @@ import org.springframework.stereotype.Component; import vip.xiaonuo.dev.modular.message.service.DevMessageService; import java.io.IOException; +import java.nio.channels.ClosedChannelException; import java.nio.charset.Charset; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; /** * 站内信WebSocket @@ -40,6 +43,31 @@ public class DevMessageWebSocket { */ private static final ConcurrentHashMap SESSION_USER_MAP = new ConcurrentHashMap<>(); + /** + * 应用关闭标志 + */ + private static final AtomicBoolean IS_SHUTTING_DOWN = new AtomicBoolean(false); + + /** + * 应用关闭时优雅关闭所有WebSocket连接 + */ + @PreDestroy + public void destroy() { + IS_SHUTTING_DOWN.set(true); + log.info("应用关闭,开始清理WebSocket连接,当前连接数: {}", SESSION_POOL.size()); + SESSION_POOL.values().forEach(session -> { + try { + if (session.isOpen()) { + session.close(new CloseReason(CloseReason.CloseCodes.GOING_AWAY, "服务器关闭")); + } + } catch (Exception e) { + // 忽略关闭时的异常 + } + }); + SESSION_POOL.clear(); + SESSION_USER_MAP.clear(); + } + @OnOpen public void onOpen(Session session) { try { @@ -82,10 +110,29 @@ public class DevMessageWebSocket { @OnError public void onError(Session session, Throwable error) { + // 应用关闭时或通道已关闭时,忽略相关异常 + if (IS_SHUTTING_DOWN.get() || isClosedException(error)) { + onClose(session); + return; + } log.error("WebSocket onError", error); onClose(session); } + /** + * 判断是否为关闭相关的异常 + */ + private boolean isClosedException(Throwable error) { + if (error instanceof ClosedChannelException) { + return true; + } + if (error instanceof IOException) { + Throwable cause = error.getCause(); + return cause instanceof ClosedChannelException; + } + return false; + } + @OnMessage public void onMessage(String message, Session session) { // 收到消息,暂不处理 diff --git a/snowy-web-app/src/main/resources/logback-spring.xml b/snowy-web-app/src/main/resources/logback-spring.xml index d7109578..90c2e72b 100644 --- a/snowy-web-app/src/main/resources/logback-spring.xml +++ b/snowy-web-app/src/main/resources/logback-spring.xml @@ -5,7 +5,7 @@ - +