1. 程式人生 > 實用技巧 >第六節:Core SignalR中的重連機制和心跳監測機制詳解

第六節:Core SignalR中的重連機制和心跳監測機制詳解

一. 重連機制

宣告:   本節僅介紹重連機制和心跳監測機制,基於Core 3.1框架,至於SignalR其它的一些基本使用,包括引入、Hub、配置等常規操作,在本節中不介紹,後續寫Core下的SignalR

1. 說明

  預設是沒有重連機制的,需要加上withAutomaticReconnect開啟重連,預設重連4次,分別時間間隔為:0、2、10和30秒 (指掉線的瞬間馬上重連、再過2s重連、再過10s重連,再過20s重連,這裡指的是間隔,而不是疊加)。當然也可以自行配置,eg:.withAutomaticReconnect([10000, 4000, 10000, 10000])。

PS: 經過測試,這裡有一個現象,比如 先斷網,觸發掉線機制,然後恢復網,走重連機制,恢復後的重連的第一次是連不上的,必須第二次才能連上。

第一次報錯:'Error: WebSocket closed with status code: 1006 ().

程式碼分享

//安卓手機的寫法
var connection = new signalR.HubConnectionBuilder().withUrl("http://47.92.198.126:8088/chathub")
                 .withAutomaticReconnect([10000, 4000, 10000, 10000])
                 .build();    
//蘋果的手機的寫法 (需要跳過協商)          
var connection = new signalR.HubConnectionBuilder().withUrl("http://47.92.198.126:8088/chathub", {
                        skipNegotiation: 
true, //針對webSocket為預設協議的時候,可以跳過協商 transport: signalR.HttpTransportType.WebSockets }) .withAutomaticReconnect([3000, 4000, 10000, 10000]) .build();

  我們發現上述程式碼,安卓和IOS寫法不一樣,這裡是因為IOS系統僅支援WebSocket協議,所以要手動指定,並且跳過協商。

2. 重連的回撥

(1).onreconnecting:重連之前呼叫 (只有在掉線的一瞬間,只進入一次),狀態為:Reconnecting 。

(2).onreconnected:(預設4次重連),任何一次只要回撥成功,呼叫,狀態為:Connected 。

(3).onclose:(預設4次重連) 全部都失敗後,呼叫,狀態為:Disconnected。

3. 實戰測試

  分析下面程式碼,建立連線後,手機斷網,輸出1,進入了onreconnecting 回撥;大約過 3s 後,連線上網,即已經過了第一次重連的時間,進入第2個 4s的重連,過了4s,走重連機制,這個時候屬於恢復網路後的第一次,是重連不上的,需要到了第三個重連機制,再過10s後,重連成功。

  如果一直斷網,4次重連全部失敗,則會進入onclose回撥。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
        <title></title>
        <script src="js/jquery.js" type="text/javascript" charset="utf-8"></script>
        <script src="js/signalr.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            $(function() {
                //蘋果的手機的寫法 (需要跳過協商)               
                var connection = new signalR.HubConnectionBuilder().withUrl("http://XXX:8088/chathub", {
                        skipNegotiation: true, //針對webSocket為預設協議的時候,可以跳過協商
                        transport: signalR.HttpTransportType.WebSockets
                    })
                    .withAutomaticReconnect([3000, 4000, 10000, 10000])
                    .build();            
                //建立連線
                $('#j_btn1').click(function() {
                    connection.start().then(function() {
                        console.log("連線成功");
                        $('#j_hb').append('<div>連線成功</div>')
                    }).catch(function(err) {
                        $('#j_hb').append('<div>' + err.toString() + '</div>')
                        return console.error(err.toString());
                    });
                });
                //傳送訊息
                $("#j_send").click(function() {
                    connection.invoke("SendMessage", $("#j_content").val()).catch(function(err) {
                        $('#j_hb').append('<div>' + err.toString() + '</div>');
                        return console.error(err.toString());
                    });

                });
                //接收訊息
                connection.on("ReceiveMessage", function(msg) {
                    console.log(msg);
                    $('#j_hb').append('<div>' + msg + '</div>')
                });

                //下面測試斷線重連機制 ,
                //重連之前呼叫 (只有在掉線的一瞬間,只進入一次)
                connection.onreconnecting((error) => {
                    console.log(1);
                    $('#j_hb').append('<div>1</div>');
                    console.log(connection.state);
                    console.log(connection.state === signalR.HubConnectionState.Reconnecting);

                });
                //(預設4次重連),任何一次只要回撥成功,呼叫
                connection.onreconnected((connectionId) => {
                    console.log(2);
                    $('#j_hb').append('<div>2</div>');
                    console.log(connection.state);
                    console.log(connection.state === signalR.HubConnectionState.Connected);

                });
                //(預設4次重連) 全部都失敗後,呼叫
                connection.onclose((error) => {
                    console.log('3');
                    $('#j_hb').append('<div>3</div>');
                    console.log(connection.state);
                    console.assert(connection.state === signalR.HubConnectionState.Disconnected);
                });
            });
        </script>
    </head>
    <body>
        <br /> <br /> <br /> <br /> <br />
        <input type="text" id="j_content" value="" />
        <button id="j_btn1">建立連線</button>
        <button id="j_send">傳送訊息</button>
        <br /> <br /> <br /> <br /> <br />
        <div id="j_hb">

        </div>
    </body>
</html>

二. 心跳監測機制

1. 何為心跳監測機制

 當客戶端和app端不傳送訊息的時候,這個時候需要一種機制,來相互ping,保證客戶端和伺服器端是連線狀態的,從而一直保證線上狀態哦。這裡有兩套機制來保證,分別是告訴客戶端,伺服器是正常的;告訴伺服器端,客戶端是線上的。SignalR提供了兩套監測機制,來保證長久連線線上不掉,或者刪掉不必要的客戶端,釋放資源。

2. 配置說明

(1). 服務端配置

  A. clientTimeoutInterval:表示客戶端如果在30s內沒有向伺服器傳送任何訊息,那麼伺服器端則會認為客戶端已經斷開連線了,則進入OnDisconnectedAsync方法, 但實際上 客戶端此時可能並沒有斷開連線,或者斷開連線還需要一段時間,因為客戶端斷開連線是走的另外一套機制的。【以伺服器端為基準,判斷客戶端是否斷開連線,從而斷開伺服器端連線】

  B. keepAliveinterval: 表示如果伺服器未在15s內向客戶端傳送訊息,在15s的時候伺服器會自動ping客戶端,是連線保持開啟的狀態。【用於控制服務端自動ping客戶端的時間】

(2). 客戶端配置

  A. serverTimeoutInMilliseconds:表示客戶端如果在30s內收到伺服器端傳送的訊息,客戶端會斷開連線,進入onclose事件。(前提是沒有啟動:自動重連機制,已測試)。 它和伺服器端的keepAliveinterval是一對的,該值必須比伺服器端的serverTimeoutInMilliseconds值大,建議是它的兩倍。是以客戶端為基準,判斷伺服器端是否斷開連線,從而斷開客戶端連線

  B. keepAliveIntervalInmillisecods:使用者控制客戶端自動ping伺服器端的時間。 指如果客戶端在15s內沒有傳送任何訊息,則15s的時候客戶端會自動ping一下伺服器端,從而告訴伺服器端,我線上。如果15s內發訊息,這個時間間隔將會被重置。

(3). 兩套機制 (實際中,兩套機制相互配合使用)

A. 以客戶端為基準的機制 (客戶端主動進 onclose回撥)

 客戶端配置:serverTimeoutInMilliseconds + 服務端端配置:keepAliveinterval ,建議serverTimeoutInMilliseconds 的值是keepAliveinterval 的兩倍,從而保證客戶端不進入 onclose回撥,不掉線。

程式碼分享:下面程式碼的配置就是預設配置,客戶端配置30s沒有收到伺服器端發過來的資訊,則認為伺服器端異常,客戶端掉線,進入onclose回撥;伺服器端配置為15s沒有向客戶端傳送訊息,則需要主動ping一下客戶端,按照預設這種Server 15s,Client 30s的配置,在網路通暢的情況下,客戶端是不會掉線的。

PS:此處如果改個非正常配置,比如客戶端serverTimeoutInMilliseconds 配置10s,伺服器端 keepAliveInterval 配置20s,經測試,你會發現,如果客戶端在10s內沒有收到任何訊息,則會弔銷,進入onclose回撥。

var connection = new signalR.HubConnectionBuilder().withUrl("http://XXXX:8088/chathub").build();    
connection.serverTimeoutInMilliseconds = 30000;  //30s 
      public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            //此處是做SignalR的全域性配置,也可以去下面的集線器出配置單獨的,單獨的優先順序> 此處全域性的
            services.AddSignalR(hubOptions =>
            {       
                //伺服器端向客戶端 ping的間隔
                hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(15);
            });
        }

B. 以服務端為基準的機制

 客戶端配置:keepAliveIntervalInmillisecods + 服務端配置:clientTimeoutInterval,建議clientTimeoutInterval 的值是keepAliveIntervalInmillisecods 的兩倍,從而保證不進伺服器端的OnDisconnectedAsync 回撥,即不掉線。

程式碼分享:下面程式碼是預設配置,客戶端配置15s內沒有向伺服器傳送任何訊息,則自動ping一下伺服器端;伺服器端配置30s沒有收到客戶端傳送的訊息,則認為客戶端已經掉線(雖然客戶端此時可能沒有掉線);在網路通暢的情況下,伺服器端是不會進入OnDisconnectedAsync回撥的。

var connection = new signalR.HubConnectionBuilder().withUrl("http://XXXX:8088/chathub").build();    
connection.keepAliveIntervalInMilliseconds= 15000;  //15s
      public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            //此處是做SignalR的全域性配置,也可以去下面的集線器出配置單獨的,單獨的優先順序> 此處全域性的
            services.AddSignalR(hubOptions =>
            {       
                //要求30s內必須收到客戶端發的一條訊息,如果沒有收到,那麼伺服器端則認為客戶端掉了
                hubOptions.ClientTimeoutInterval= TimeSpan.FromSeconds(30);
            });
        }

3. 實戰測試結論

PS:要把自動重連的機制關了再測試,否則會干擾,無法進行。

(1).正常配置下(指滿足前後端大小關係的配置,不必非2倍關係),劃掉應用,伺服器端會立刻進入 OnDisconnectedAsync 回撥。

(2).正常配置下(指滿足前後端大小關係的配置,不必非2倍關係),客戶端斷網,伺服器端會在 clientTimeoutInterval 中配置的時間內 進入 OnDisconnectedAsync回撥。

(3). 非正常配置,

伺服器端:clientTimeoutInterval = 5s

客戶端:keepAliveIntervalInmillisecods=15s

經測試:客戶端建立連線,傳送了一條訊息,再不發訊息,也不斷網, 這個時候過了 15s, 客戶端會自動ping一下伺服器端,然後再過5s,伺服器端進入 OnDisconnectedAsync 回撥。

下面在分享一下 客戶端和伺服器端相互ping的截圖:

綠色箭頭代表 客戶端ping Server端, 紅色箭頭代表 Server端ping 客戶端)

**********轉摘:https://www.cnblogs.com/yaopengfei/p/12622715.html