首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

WebSocket 異步發送陷阱:Session 作用域與線程上下文導致 sendText 失效

     編輯:程式設計綜合問答

故障現象

在 @OnMessage 方法中直接調用 session.getBasicRemote().sendText() 正常;但封裝到工具類 WebSocketUtil.send() 中調用同樣代碼,前端收不到消息,無報錯。

根因:異步發送與 Session 生命週期

  1. getBasicRemote().sendText() 是非阻塞異步操作,返回 Future 或直接排隊發送。
  2. @OnMessage 執行時,WebSocket 容器保證 Session 在線程上下文中有效,發送隊列綁定當前調用鏈。
  3. 調用工具類時,若工具類方法異步執行(如提交到線程池、新線程),Session 可能已關閉、或不在 WebSocket 容器管理的上下文中,導致發送隊列丟失。
  4. 即使同步調用,若工具類內部再次切換線程(如 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); }
}

方案二:使用 AsyncRemote 異步 API 正確處理

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 由容器管理線程上下文,安全跨線程調用。

方案三:廣播場景 - 維護 Session 集合,在容器線程中迭代發送

// 端點類中維護靜態集合
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 洩漏到業務線程池。
  • ✅ 發送前檢查 session.isOpen(),捕獲 IOException 並移除失效 Session。

總結

WebSocket 發送失效 90% 由「異步調用切換線程上下文」導致。鐵律:發送動作留在容器線程,業務計算可拋線程池。用 AsyncRemote 橋接異步業務與同步發送,徹底解決工具類發送無效問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved