系統中有多個查詢線程分別輪詢不同設備獲取資料,當使用者開啟網頁時會自動建立 WebSocket 連線。查詢線程與 WebSocket 連線管理屬於不同執行緒,如何在查詢線程中呼叫 WebSocket 的 send 方法將即時資料推送給前端?
採用「生產者-消費者」模式,後端維護一個線程安全的 Map<String, Session>(或 ConcurrentHashMap),Key 為使用者唯一標識(如 userId、deviceId),Value 為對應的 javax.websocket.Session 物件。
@OnOpen 方法中將 Session 放入 Map。@OnClose / @OnError 移除 Map 中對應條目,防止記憶體洩漏。Session,呼叫 session.getBasicRemote().sendText(json) 或 sendBinary。@ServerEndpoint("/websocket/{userId}")
@Component // Spring 管理
public class DeviceWebSocket {
private static final ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
sessions.put(userId, session);
}
@OnClose
public void onClose(@PathParam("userId") String userId) {
sessions.remove(userId);
}
/** 供查詢線程呼叫的靜態推送方法 */
public static void pushData(String userId, String jsonPayload) {
Session session = sessions.get(userId);
if (session != null && session.isOpen()) {
try {
session.getBasicRemote().sendText(jsonPayload);
} catch (IOException e) {
log.error("WebSocket 推送失敗: {}", e.getMessage());
sessions.remove(userId); // 發送失敗視為連線異常,清理
}
}
}
}
// 查詢線程內部
String payload = buildJson(deviceId, sensorData);
DeviceWebSocket.pushData(targetUserId, payload);
sessions.values().forEach(s -> ...),注意效能與背壓。@OnError 清理殭屍 Session。session.getAsyncRemote() 避免阻塞查詢線程。核心在於「共享 Session 註冊表」+「線程安全存取」。查詢線程只需持有使用者標識,即可隨時從註冊表取得 Session 並推送資料,實現真正的伺服器主動推送。