欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

.NET WebSocket高并發(fā)通信阻塞問(wèn)題

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

項(xiàng)目上遇到使用WebSocket超時(shí)問(wèn)題,具體情況是這樣的,OTA升級(jí)過(guò)程中,解壓zip文件會(huì)有解壓進(jìn)度事件,將解壓進(jìn)度通過(guò)進(jìn)程通信傳給另一進(jìn)程,通信提示超時(shí)異常

小伙伴堂園發(fā)現(xiàn)大文件使用Zip解壓,解壓進(jìn)度事件間隔竟然是1ms,簡(jiǎn)直超大頻率啊

但是,解壓事件超頻也不應(yīng)該通信異常啊,于是我通過(guò)1ms定時(shí)發(fā)送通信事件,測(cè)試了下進(jìn)程間通信流程。

WebSocketSharp

當(dāng)前進(jìn)程間通信組件是基于kaistseo/UnitySocketIO-WebSocketSharp實(shí)現(xiàn),主機(jī)內(nèi)設(shè)置一服務(wù)端,多個(gè)客戶端連接服務(wù)端,客戶端通信由服務(wù)端轉(zhuǎn)發(fā)數(shù)據(jù)。客戶端A發(fā)送給B后,客戶端B會(huì)將執(zhí)行結(jié)果反饋給客戶A。

那在定位中發(fā)現(xiàn),各個(gè)鏈路發(fā)送延時(shí)都是正常的,包括服務(wù)端發(fā)送反饋數(shù)據(jù)給到客戶端A,但客戶端A接收數(shù)據(jù)延時(shí)很大,下面是部分返回?cái)?shù)據(jù):

并且通信時(shí)間久了之后,延時(shí)會(huì)越來(lái)越大

 

這里是WebSocketSharp.WebSocket對(duì)外事件OnMessage: 

private void WebSocketOnMessage(object sender, MessageEventArgs e)

    {

        if (!e.IsText)

        {

            //暫時(shí)不支持

            return;

        }

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


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

        xxxxx

    }

我們繼續(xù)往下看,OnMessage是由WebSocket.message()觸發(fā),從_messageEventQueue隊(duì)列中獲取數(shù)據(jù): 

private void message ()

    {

      MessageEventArgs e = null;

      lock (_forMessageEventQueue) {

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

          return;


        _inMessage = true;

        e = _messageEventQueue.Dequeue ();

      }


      _message (e);

    }

循環(huán)接收數(shù)據(jù)是這樣拿的: 

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。。。數(shù)據(jù)量那么大,這里搞個(gè)同步信號(hào)鎖,肯定會(huì)堵住咯

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

WebSocketSharp數(shù)據(jù)發(fā)送是基于TCPClient實(shí)現(xiàn)的:

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

    _stream = _tcpClient.GetStream ();

初始化后通過(guò)_stream.Write (bytes, 0, bytes.Length);發(fā)送數(shù)據(jù)

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

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

但接收時(shí)在高并發(fā)場(chǎng)景下,適當(dāng)?shù)耐酱胧┮廊皇潜匦璧摹N覀兛梢允褂胠ock也可以用SemaphoreSlim來(lái)實(shí)現(xiàn)復(fù)雜的同步需求,這里使用的是信號(hào)鎖ManualResetEvent

我們?cè)倏纯窗l(fā)送端代碼,也是用了lock一個(gè)object來(lái)限制并發(fā)操作:

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在高并發(fā)場(chǎng)景下是存在通信阻塞問(wèn)題的。當(dāng)然,WebSocketSharp已經(jīng)實(shí)現(xiàn)的很好了,正常的話幾ms都不會(huì)遇到阻塞問(wèn)題,如下設(shè)置3ms定時(shí)超頻發(fā)送、發(fā)送一段時(shí)間后:

客戶端A發(fā)送消息,由服務(wù)端轉(zhuǎn)發(fā)至客戶B,再將客戶端B的反饋結(jié)果由服務(wù)端轉(zhuǎn)發(fā)回客戶端A,真正延時(shí)才0-2ms!

 

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

WebSocket

我們?cè)倏纯丛腤ebSocket,寫個(gè)WebSocket通信Demo kybs00/WebSocketDemo (github.com)

服務(wù)端定時(shí)1ms使勁往客戶端發(fā)送Message消息,結(jié)果竟然是:

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.”

看來(lái)發(fā)送事件外部也要處理好高并發(fā)的場(chǎng)景,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);

    }


加完信號(hào)量同步,服務(wù)端就能正常發(fā)送了。下面是10分鐘后客戶端接收數(shù)據(jù)打印,傳輸幾乎無(wú)延時(shí):

另外,也嘗試了單獨(dú)在客戶端接收添加信號(hào)量同步,依然是提示服務(wù)端發(fā)送不支持并行的異常。

所以原生WebSocket在發(fā)送端加個(gè)需要串行處理比如上面的SemaphoreSlim信號(hào)量,保證完整的寫入完數(shù)據(jù)、執(zhí)行_stream.FlushAsync()。

 

作者:唐宋元明清2188

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

本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須在文章頁(yè)面給出原文連接,否則保留追究法律責(zé)任的權(quán)利。



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