在實做SignalR遠端遙控的過程有個疑問: 若在連線過程網站重啟或因故障中斷服務,SignalR是否會在服務恢復後自動重建連線? Client需不需要特殊寫法配合?

依據網路上的討論,SignalR Client本身已內建重連機制,連線中斷後會自動重連,理論上不需要額外費心寫Code處理。不過我還是決定實測一下,確保程式行為如同預期比較安心。

前次遠端遙控用的CommHub加以簡化,在Connect/Reconnect事件中觸發Client端的ShowMessage()方法顯示訊息,另外增加一個HeartBeat()方法,供Client定期呼叫,藉以偵測連線運作正常。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using SignalR;
using SignalR.Hubs;
using System.Threading.Tasks;
 
namespace HubWeb.Models
{
    public class CommHub : Hub, IDisconnect, IConnected
    {
        //連線關閉時觸發
        public Task Disconnect()
        {
            return null;
        }
        //連線建立時觸發
        public Task Connect()
        {
            string cid = Context.ConnectionId;
            Clients[cid].ShowMessage("Hello");
            return null;
        }
        //重新連線時觸發
        public Task Reconnect(IEnumerable<string> groups)
        {
            string cid = Context.ConnectionId;
            Clients[cid].ShowMessage("Reconnect");
            return null;
        }
        //偵測連線狀態
        public void HeartBeat()
        {
            string cid = Context.ConnectionId;
            Clients[cid].ShowMessage(
                string.Format("HearBeat@{0:mm:ss}", DateTime.Now));
        }
    }
}

Client端的邏輯為: 宣告連線、建立Proxy、註冊"ShowMessage”事件Console.WriteLine傳入的訊息、接著connection.Start()啟動連線、使用無窮迴圈定期呼叫HeartBeat(為求簡化沒寫跳出邏輯,測到高興後使用Ctrl-C大絕結束程式)。

應用前篇文章介紹過的.NET 4.0 Task.ContinueWith()及Task.Result在連線完成後開始每兩秒一次的commHub.HeartBeat()呼叫,因執行SignalR Server端方法是非同步進行的,我們在ContinueWith()加入Task.IsFaulted檢查,攔截並顯示錯誤內容,再透過Task.Result特性於非同步執行完畢後顯示HeartBeat失敗。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using SignalR.Client.Hubs;
using System.Threading.Tasks;
 
namespace ConsoleClient
{
    class Program
    {
        static void Main(string[] args)
        {
            //連線SignalR Hub
            var connection = new HubConnection("http://localhost:26217/");
            IHubProxy commHub = connection.CreateProxy("CommHub");
            //顯示Hub傳入的文字訊息
            commHub.On("ShowMessage", msg => Console.WriteLine(msg));
            //建立連線,連線建立完成後向Hub註冊識別名稱
            var init = connection.Start().ContinueWith<bool>(task =>
            {
                //若連線失敗,顯示錯誤並return false;
                if (task.IsFaulted)
                {
                    foreach (var e in 
                             task.Exception.Flatten().InnerExceptions)
                        Console.WriteLine("Error: " + e.Message);
                    return false;
                }
                //若未出錯,則return true 通過檢查後才能繼續執行
                return true;
            });
            //利用Task.Result的同步化等待,待連線完成後才續持
            if (!init.Result) 
                return;
            //持續執行,如要結束就用Ctrl-Break大絕
            while (true)
            {
                var hb = 
                    commHub.Invoke("HeartBeat").ContinueWith<bool>(task =>
                    {
                        //出錯時傳回false; 否則傳回true;
                        if (task.IsFaulted)
                        {
                            foreach (var ex in 
                                     task.Exception.Flatten().InnerExceptions)
                            {
                                Console.WriteLine("Error: {0}", ex.Message);
                            }
                            return false;
                        }
                        return true;
                    });
                if (!hb.Result)
                    Console.WriteLine("HeartBeat Failed");
                Thread.Sleep(2000);
            }
        }
    }
}

實測結果如下圖。連線後Connect事件被觸發回呼Client顯示"Hello",接著開始每兩秒顯示一次無窮無盡的HeartBeat訊息。故意停止CommHub所在的IIS Express,則如預期出現"Unable to connect to the remote server”及心跳失敗錯誤訊息,接著重新啟動IIS Express,薑薑薑薑~ 心跳目動恢復,Reconnect事件也被觸發,驗證了SignalR的自動重連功能很好很強大,可以安心地應用在系統中嚕!


Comments

# by 1075715442@qq.com

有没有测试 长时间断开的话,重新开启IIS。也会自动重连吗? 有没有一个时间间隔?

# by 棉花

自己試了一下 Client連線後關閉IIS,Client會嘗試重新連接 到達指定的timeout時間(預設30秒)後,會直接關閉連線 文章中黑大只把IIS斷開13秒,所以不會觸發到關閉連線 有錯的話歡迎指正

# by Jeffrey

to 棉花,感謝細心測試與分享。

# by Tom

A better answer about this issue https://forums.asp.net/t/2005835.aspx?How+to+control+SignalR+reconnection+on+server+restart+

# by

大大好 我使用兩台機器進行Load balace+Redis Scaleout, 在applicaition pool 進行回收的時候, 發現reconnected會一直failed /signalr/reconnect?transport=serverSentEvents&groupsToken=tsrqvEu0aC12s%2BgQOmj1ULslTEmenhpSmAdzB8jT2wLaoQ%2F75GbAEBxdpIkv7UqsolR9R0%2BbMSJtXKpCScGv9hyEA6kMZkuQQXwNVeq4p82t8Wa8pVe9Zp1W0hy7A0t5YjPm6B1BIda9%2BKIJxBiIAEvSE2PXFpT4FUfF9xxuFuk%3D&messageId=s-0%2C83C4F9F&clientProtocol=1.5&connectionToken=bQdHDBcr25x5EXuh46YdQjZpjZelvyj7cxFO%2FR6iW9Ysj1CA8CkZXaxlU6ZqoGLrGpJYG1tDFStcba8AI%2BSiRkdQLiyh%2Fn%2BgiwBe2wGuDnd%2F%2FGNepKz8Sq5PDFwAjPAe&connectionData=%5B%7B%22name%22%3A%22deadballhub%22%7D%5D&tid=10 net::ERR_CONNECTION_RESET 200 (OK) 請問大大有甚麼想法嗎?謝謝

# by Jeffrey

to 海,SignalR Scaleout 我沒經驗,幫不上忙。

Post a comment