目錄:
項目背景
本篇記錄WebSocket :用WebSocket實現推送你必須考慮的幾個問題 onError錯誤用法導致的一個bug(同一種client類型只能登陸一個設備,具體代碼可以參見 : http://download.csdn.net/download/shangmingtao/9920532) ,代碼:
/*
Close
*/
@OnClose
public void onClose(@PathParam("userId") String userId,
Session session) {
log.info("[WebSocketServer] Close Connection : userId = " + userId);
WebSocketUtils.remove(userId);
}
/*
Error
*/
@OnError
public void onError(@PathParam("userId") String userId,
Throwable throwable,
Session session) {
log.info("[WebSocketServer] Connection Exception : userId = "+ userId + " , throwable = " + throwable.getMessage());
WebSocketUtils.remove(userId);//清除userId和session對應關係
}
WebSocket連接流程圖:
bug復現條件 :
client端連接上WebSocket後斷開網絡 ->打開網絡 ->重新連接.
bug現象:
服務端拋出TCP reset異常, reset異常觸發onError回調方法, 上述代碼中onError中會清除userId和session的對應關係.但實際清除的並不是舊鏈接的session和userId的對應關係.因爲我們用map或者redis存儲userId和session對應關係時是一個K-V存儲,新的會覆蓋舊的.
bug原因:
斷開網絡前:
斷開網絡後:
造成RST包的原因 :
- 端口未打開
- 請求超時
- 提前關閉 (當本端斷開連接(不論什麼原因TCP四次揮手未到達對端),另一端發送消息到本端會觸發本端回覆RST包),這也是本bug原因.至於[PSH,ACK]具體是那條消息的ACK我沒有深究.
解決思路&方案:
很多網上WebSocket服務端代碼對於生產環境應用來講都誤導了大家,onClose方法和onError方法處理一模一樣.但實際這兩個方法分別是不同情況的回調.一個是關閉一個是異常.雖然很多時候觸發onError方法後會觸發onClose.比如網絡異常導致連接異常,然後ws關閉了連接.但是也有一些情況是僅觸發onError方法.比如上邊的server端close掉連接,然後接到RST包這種情況.
所以我們的處理方案是在onError回調中僅打印一條日誌或者針對不同的異常寫邏輯.無論怎麼處理都不可以在onError方法中接觸userId和session之間的對應關係.