在 @OnMessage 方法中直接調用 session.getBasicRemote().sendText() 正常;但封裝到工具類 WebSocketUtil.send() 中調用同樣代碼,前端收不到消息,無報錯。
getBasicRemote().sendText() 是非阻塞異步操作,返回 Future 或直接排隊發送。@OnMessage 執行時,WebSocket 容器保證 Session 在線程上下文中有效,發送隊列綁定當前調用鏈。Session 可能已關閉、或不在 WebSocket 容器管理的上下文中,導致發送隊列丟失。CompletableFuture.runAsync),同樣會丟失上下文。@OnMessage
public void onMessage(String message, Session session) {
// 直接在這裡處理業務邏輯,不要拋到其他線程
WebSocketUtil.sendSync(session, game); // 傳入 session,同步調用
}
// 工具類
public static void sendSync(Session session, Game game) {
try {
session.getBasicRemote().sendText(toJson(game));
} catch (IOException e) { log.error(e); }
}
public static void sendAsync(Session session, Game game) {
session.getAsyncRemote().sendText(toJson(game), new SendHandler() {
@Override public void onResult(SendResult result) {
if (result.isOK()) log.debug("Sent");
else log.error("Send failed", result.getException());
}
});
}
AsyncRemote 由容器管理線程上下文,安全跨線程調用。
// 端點類中維護靜態集合
private static final CopyOnWriteArraySet sessions = new CopyOnWriteArraySet<>();
@OnOpen public void onOpen(Session s) { sessions.add(s); }
@OnClose public void onClose(Session s) { sessions.remove(s); }
@OnMessage public void onMessage(String msg, Session s) {
// 業務邏輯確定目標會話集合 targetSessions
for (Session target : targetSessions) {
// 必須在當前容器線程中調用
target.getAsyncRemote().sendText(payload);
}
}
sendText 調用必須發生在 WebSocket 容器調度的線程中(@OnMessage/@OnOpen/@OnClose 內,或容器派生的任務中)。session.getAsyncRemote()。session.isOpen(),捕獲 IOException 並移除失效 Session。WebSocket 發送失效 90% 由「異步調用切換線程上下文」導致。鐵律:發送動作留在容器線程,業務計算可拋線程池。用 AsyncRemote 橋接異步業務與同步發送,徹底解決工具類發送無效問題。