開發框架
- springMVC
- tomcat8
問題描述
后端建立websocket 前端連接上來,后台會主動推送agent腳本執行信息,由於采用netty框架,保證並發性,執行的結果是多線程處理的,通過websocket返回前端居然報錯了,很是費解。症狀見下圖。
排查解決過程
從圖中可以看出,遠端處於【TEXT_PARTIAL_WRITING】狀態,就這這個關鍵字google(不得不說就英文搜索而言,google強太多),
最后發現,tomcat的websocket沒有相關的多線程處理,有人在apache上提bug,可以看一下開發者給的回復:
tomcat8 websocker bug
I have some sympathy with this view. Given that the Javadoc for RemoteEndpoint.Basic explicitly states that
concurrent attempts to send messages are not allowed and the Javadoc for RemoteEndpoint.Async doesn't, you'd be
forgiven for thinking that meant that RemoteEndpoint.Async permitted concurrent attempts to send messages.
Unfortunately, the (not very well documented) intention of the WebSocket EG was not to allow concurrent attempts to send messages. [1]
I did take a quick look at adding an option to relax (i.e. ignoring) this but the change required to do this with the current Tomcat code would be far from trivial.
I'm marking this as INVALID since the intention of the EG was to not allow this but WONTFIX is almost as appropriate if this is viewed as an enhancement request.
As an aside, one reason not to implement this particular enhancement is that apps that depended on it would not be portable between containers.
提bug的人提出了一個很常見的需求:
一個簡單的聊天應用程序與3個用戶聊天將導致並發調用Async.sendText()當A和B都發送一個必須傳遞到C的消息,所以用例是很常見的。
應用程序擔心同步或排隊並發調用Async.sendText()會對執行並發寫入的應用程序造成巨大的負擔,我相信這不是JSR 356的意圖(盡管規范並沒有說明並發保證) 。
tomcat 開發者大致認為
javaDoc,RemoteEndpoint.Basic明確指出不允許並發發送消息,但是RemoteEndpoint.Async沒有,並且websocket eg 的意圖是不允許並發,最終結論將此bug定位為無效bug
解決方法
看下面的評論也是很有意思,哈哈,大部分人認為容器應該為應用程序開發者提供便利,提供容器內部的緩沖,,而不是程序開發者自己管理websocket Session緩沖區。有一個方法是利用同步來降低這種問題的出現頻率,與此同時,Tyrus 1.5是安全的,貌似只有tomcat存在這個問題。
隨之而來的新的問題
采用同步操作之后,由於agent會有很多中間結果,當第一條消息到來把鎖搶占之后,后面的結果已經到來,當鎖釋放之后,其他線程搶占鎖,尼瑪,原先有時間順序的結果變得混亂無序. 難道真的要自己實現一套緩沖隊列嗎。左思右想,最終決定還是有公平鎖好了
公平鎖是個好東西,自己內部實現了一套等待隊列,雖然效率有點低,但是對業務來說可以滿足需求
private static ReentrantLock lock = new ReentrantLock(true);