實測SignalR自動重連特性
| | | 6 | |
在實做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 我沒經驗,幫不上忙。