爲什麼你的websocket只能建立256個連接?

WebSocket想必大家都不陌生,當我們的程序需要實時高效的獲取後端的返回結果時,除了早期大家用到的前端輪循的機制之外,當前比較簡單好用的莫過於WebSocket了。

當然,這篇文章不是WebSocket的科普文,按照慣例:強哥出品,必屬精品的原則(哈哈,自吹一波)。我們就不在這裏介紹WebSocket是什麼以及如何在Springboot上使用了,大家有興趣的可以自行百度。

當我們在後端項目中加入WebSocket之後,如何測試是否配置成功呢?無疑編寫一個前端html5的demo頁面,試着建立WebSocket連接並由後端主動推送消息查看前端是否收到是最簡單的驗證方式了。

截取部分後端代碼:


@OnOpen
public void onOpen(Session session,@PathParam("winNum") String fromWinNum) throws IOException {
    this.session = session;
    if(StringUtils.isEmpty(fromWinNum)){
        log.error("請輸入窗口號!!!!!!!!!!!!!!!!");
        return;
    }else{
        try {
            if(websocketList.get(fromWinNum) == null){
                this.winNum = fromWinNum;
                websocketList.put(fromWinNum,this);
                addOnlineCount();           //在線數加1
                log.info("有新窗口開始監聽:{},當前窗口數爲{}",fromWinNum,getOnlineCount());
                sendMessage("一條來自後端的消息");
            }else{
                session.getBasicRemote().sendText("已有相同窗口,請重新輸入不同窗口號");
                CloseReason closeReason = new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE,"相同窗口");
                session.close(closeReason);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    if(session.isOpen()){
        log.info("connect success");
    }
}

下面是一個簡單的前端腳本:


<!DOCTYPE HTML>
<html>
   <head>
   <meta charset="utf-8">
      <script type="text/javascript">
         var success = 0;
         function WebSocketTest(i){
           if ("WebSocket" in window){      
            var x = 100;//上限
            var y = 0; //下限
             // 打開一個 web socket
            var ws = new WebSocket("ws://localhost:8080/websocket/" + i);
            ws.onopen = function(){
              // Web Socket 已連接上,使用 send() 方法發送數據
              ws.send("客戶端來了");
              success++;
              console.log("成功連接個數:"+success);
             };
             ws.onmessage = function (evt) { 
               var received_msg = evt.data;
             };
            }else{
               // 瀏覽器不支持 WebSocket
               alert("您的瀏覽器不支持 WebSocket!");
            }
         }
         WebSocketTest(1);
</script>  
   </head>
   <body>
   </body>
</html>

可以看到,我們在js代碼中,調用WebSocketTest()方法創建了前端Html5的WebSocket客戶端並連接到服務器端。我們打開Chrome瀏覽器的開發者工具查看:

確實成功創建了WebSocket連接,同時收到了來自後端推送的消息。由此可見,我們的項目中引入WebSocket其實還是相對比較簡單的,同時因爲其能夠在前端主動收到來自後端推送的消息,實時性上也更優於前端輪詢的機制。

當我們完成了上面的內容,想必許多人就急急忙忙的開始在WebSocket服務中加入自己的業務邏輯了。強哥當初也是這樣,可是代碼敲着敲着心裏就暗暗發虛。

不知道大家有沒有思考過一個問題:WebSocket是長連接,當用戶量上來後,對我們服務端的壓力不是就會比傳統的Http無狀態連接的壓力大嗎?要是因爲引入了WebSocket後,用戶量一上來就導致系統無法訪問那可不行,那不是還不如原來的輪詢了嗎?

沒錯,如果能夠在項目中引入新功能時,我們能夠多想想,我相信你已經是一個較負責的開發人員了。儘量讓問題發生在開發甚至預研階段,而不是在項目真正的部署上線後往往是比較的,上線後遇到問題,只會讓我們焦頭爛額並且備受打擊。

既然擔心引入WebSocket會對性能造成影響,那最直接方式無疑就是進行壓測看看實際效果了。那麼要怎麼壓測呢?哈哈,我們不是已經有了前端連接WebSocket的代碼了嗎,我們進行改造一下:

<!DOCTYPE HTML>
<html>
   <head>
   <meta charset="utf-8">
      <script type="text/javascript">
         var success = 0;
         function WebSocketTest(i){
            if ("WebSocket" in window)
            {
              var x = 100;//上限
              var y = 0; //下限
               // 打開一個 web socket
               var ws = new WebSocket("ws://localhost:8080/websocket/" + i);
               ws.onopen = function(){
                  // Web Socket 已連接上,使用 send() 方法發送數據
                  ws.send("客戶端來了");
                  success++;
                  console.log("成功連接個數:"+success);
               };
               ws.onmessage = function (evt) { 
                  var received_msg = evt.data;
               };            
            }
            else
            {
               // 瀏覽器不支持 WebSocket
               alert("您的瀏覽器不支持 WebSocket!");
            }
         }
         for(let i=0; i<50; i++){
          WebSocketTest(i)
         }
</script>
   </head>
   <body>
   </body>
</html>

 沒錯,我們將WebSocketTest()方法加入到一個for循環裏面,先循環50次練練手。前後端輸出如下:

可見沒有問題,加大壓力150再試試:

同樣沒有問題,那就翻個倍300,嘿嘿,真是越來越自信:

納尼!!!怎麼只有255個,有45個失敗了:

這尼瑪,不到300個就翻車了,那還搞什麼,難不成引入WebSocket後,我的項目只能不到300人訪問,那還不如輪訓呢。不!我不信,再來幾次:280次、310次、400次。結果居然都和上面一樣,成功建立256個連接之後,其他的就都失敗了。

256這麼詭異的數字,裏面一定隱含着驚天的祕密,第一個想法就是:難不成是後端限制了websocket的連接個數?

結果一番搜索,在配置文件中找到了如下配置:

server.tomcat.max-connections=10000

最大連接數爲10000???我壓測的結果是256。10000???256???這差的也太多了吧!!

難道是沒有生效,把10000改成10試試,前端還是一下請求300次看看:

確實連上10個之後,其他的就都失敗了,由此可見,這個配置還是生效的。那到底是因爲什麼?

連接到了256個就失敗了,後端配置最大連接數又是生效的且遠大於256,那麼這個詭異的256究竟是爲什麼呢?後端配置是對的……啊!前端是不是有什麼問題?難道瀏覽器限制了WebSocket的連接數?趕緊百度一下:

確認過眼神,就是這個答案了,Chrome瀏覽器限制WebSocket的最大連接數就是256。可能在試驗中存在一點點偏差,可是是這個答案無疑了。既然是因爲瀏覽器的限制,那我們在搞個別的瀏覽器(這裏用360瀏覽器)一起試試,後端連接改回10000,前端代碼還是300,當然因爲後端代碼有限制map的key不能重複,所以換瀏覽器時,for循環代碼需要改成從300開始,結果如下:

成功了!!後端接收了255+256=511個請求,可見後端還是能夠頂得住壓力的。由此,我們便弄清了爲什麼在測試的時候,WebSocket最多隻能連接256個連接的問題:原來各個瀏覽器有對WebSocket的連接數進行了限制。

看到這裏你可能會覺得真是一頓操作猛如虎,結果竟然是因爲瀏覽器的問題。這不是很簡單嗎?可是,當你真正的遇到問題的時候,其實,解決問題的過程纔是比較關鍵的。

既然解決了這個問題,可是接下來是不是就一帆風順了呢?強哥又想再多問幾個問題:

  • 後端既然可以接受10000個連接,可是,是不是每一個請求過來,都需要一個線程來處理呢?

  • 我們的後端代碼中,session作爲成員變量,難道是每個請求都有獨自的類處理嗎,Springboot不是默認單例嗎?

  • 前後端WebSoket有沒有可能出現斷連的問題,要如何解決呢?

這些問題,強哥在之後的推文中將會繼續展開講解,好了,今天的篇幅有點太長了,就到這裏吧。

對了,今天的源碼稍後也會發布出來,公衆號後臺留言:websocket可以獲取到相應源碼

關注公衆號獲取更多內容,有問題也可在公衆號提問哦:

強哥叨逼叨

叨逼叨編程、互聯網的見解和新鮮事

發佈了55 篇原創文章 · 獲贊 72 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章