狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

.NET WebSocket高并發通信阻塞問題

freeflydom
2024年9月5日 11:59 本文熱度 829

項目上遇到使用WebSocket超時問題,具體情況是這樣的,OTA升級過程中,解壓zip文件會有解壓進度事件,將解壓進度通過進程通信傳給另一進程,通信提示超時異常

小伙伴堂園發現大文件使用Zip解壓,解壓進度事件間隔竟然是1ms,簡直超大頻率啊

但是,解壓事件超頻也不應該通信異常啊,于是我通過1ms定時發送通信事件,測試了下進程間通信流程。

WebSocketSharp

當前進程間通信組件是基于kaistseo/UnitySocketIO-WebSocketSharp實現,主機內設置一服務端,多個客戶端連接服務端,客戶端通信由服務端轉發數據。客戶端A發送給B后,客戶端B會將執行結果反饋給客戶A。

那在定位中發現,各個鏈路發送延時都是正常的,包括服務端發送反饋數據給到客戶端A,但客戶端A接收數據延時很大,下面是部分返回數據:

并且通信時間久了之后,延時會越來越大

 

這里是WebSocketSharp.WebSocket對外事件OnMessage: 

private void WebSocketOnMessage(object sender, MessageEventArgs e)

    {

        if (!e.IsText)

        {

            //暫時不支持

            return;

        }

        Debug.WriteLine($"{DateTime.Now.ToString("HH:mm:ss fff")},{e.Data}");


        var receivedMessage = JsonConvertSlim.Decode<ChannelServerMessage>(e.Data);

        xxxxx

    }

我們繼續往下看,OnMessage是由WebSocket.message()觸發,從_messageEventQueue隊列中獲取數據: 

private void message ()

    {

      MessageEventArgs e = null;

      lock (_forMessageEventQueue) {

        if (_inMessage || _messageEventQueue.Count == 0 || _readyState != WebSocketState.Open)

          return;


        _inMessage = true;

        e = _messageEventQueue.Dequeue ();

      }


      _message (e);

    }

循環接收數據是這樣拿的: 

private void startReceiving ()

    {

      xxxx

      _receivingExited = new ManualResetEvent (false);

      Action receive = () => WebSocketFrame.ReadFrameAsync (_stream, false,

            frame => {

              if (!processReceivedFrame (frame) || _readyState == WebSocketState.Closed) {

                var exited = _receivingExited;

                if (exited != null)

                  exited.Set ();

                return;

              }

              // Receive next asap because the Ping or Close needs a response to it.

              receive ();

              xxxx

              message ();

            },

            xxxx

          );

      receive ();

    }


這里我看到了ManualResetEvent。。。數據量那么大,這里搞個同步信號鎖,肯定會堵住咯

為何設置線程同步鎖呢?我們往下看

WebSocketSharp數據發送是基于TCPClient實現的:

     _tcpClient = new TcpClient (_proxyUri.DnsSafeHost, _proxyUri.Port);

    _stream = _tcpClient.GetStream ();

初始化后通過_stream.Write (bytes, 0, bytes.Length);發送數據

接收數據,也是通過_stream讀取,可以看上方的startReceiving()方法里,WebSocketFrame.ReadFrameAsync (_stream, false,...)

我們知道,TCP是面向連接,提供可靠、順序的數據流傳輸。用于一對一的通信,即一個TCP連接只能有一個發送方和一個接收方。具體的可以看我之前寫的文章:.NET TCP、UDP、Socket、WebSocket

但接收時在高并發場景下,適當的同步措施依然是必需的。我們可以使用lock也可以用SemaphoreSlim來實現復雜的同步需求,這里使用的是信號鎖ManualResetEvent

我們再看看發送端代碼,也是用了lock一個object來限制并發操作:

private bool send (Opcode opcode, Stream stream)

    {

      lock (_forSend) {

        var src = stream;

        var compressed = false;

        var sent = false;

        xxxxx

        sent = send (opcode, stream, compressed);

        xxxxx

        return sent;

      }

    }


所以WebSocketSharp在高并發場景下是存在通信阻塞問題的。當然,WebSocketSharp已經實現的很好了,正常的話幾ms都不會遇到阻塞問題,如下設置3ms定時超頻發送、發送一段時間后:

客戶端A發送消息,由服務端轉發至客戶B,再將客戶端B的反饋結果由服務端轉發回客戶端A,真正延時才0-2ms!

 

所以上方項目中遇到的ZIP文件解壓進度超快1ms,只能要ZIP解壓處優化下,設置并發操作10ms內保留最后一個操作,可以參考 .NET異步并發操作,只保留最后一次操作,即10ms最多觸發一次解壓進度事件。確實也應該這么優化,通信即使撐住這種高并發,UI刷新這么高幀率也有點浪費CPU/GPU資源。

WebSocket

我們再看看原生的WebSocket,寫個WebSocket通信Demo kybs00/WebSocketDemo (github.com)

服務端定時1ms使勁往客戶端發送Message消息,結果竟然是:

System.InvalidOperationException:“There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can be called simultaneously, but at most one outstanding operation for each of them is allowed at the same time.”

看來發送事件外部也要處理好高并發的場景,1ms真的是太猛了

private SemaphoreSlim _sendLock = new SemaphoreSlim(1);

    private async void Timer_Elapsed(object sender, ElapsedEventArgs e)

    {

        var message = $"{DateTime.Now.ToString("HH:mm:ss fff")},hello from server";


        await _sendLock.WaitAsync();

        await BroadcastAsync("test", message);

        _sendLock.Release();

        Console.WriteLine(message);

    }


加完信號量同步,服務端就能正常發送了。下面是10分鐘后客戶端接收數據打印,傳輸幾乎無延時:

另外,也嘗試了單獨在客戶端接收添加信號量同步,依然是提示服務端發送不支持并行的異常。

所以原生WebSocket在發送端加個需要串行處理比如上面的SemaphoreSlim信號量,保證完整的寫入完數據、執行_stream.FlushAsync()。

 

作者:唐宋元明清2188

出處:http://www.cnblogs.com/kybs0/

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須在文章頁面給出原文連接,否則保留追究法律責任的權利。



該文章在 2024/9/5 12:01:31 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved