• 第七节: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 客户端)

     

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    【数据结构】线段树(Segment Tree)
    c++基础--数字读入及优化
    转:async异步、thread多线程
    走进 Akka.NET
    基于 Docker 的 DevOps 搭建
    (翻译)与.NET容器映像保持同步
    (翻译)使用 AppCenter 持续输出导出到 Application Insights
    (翻译)Xamarin.Essentials 最新预览版的更多跨平台 API
    (翻译)在 Xamarin 应用中使用 MongoDB
    (翻译)一起使用 .NET 和 Docker——DockerCon 2018 更新
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/12622715.html
Copyright © 2020-2023  润新知