[英]Server-side SignalR connection fails after significant uptime
I've searched numerous other questions related to SignalR connections on StackOverflow, but none of them seem to apply to my specific case.我在 StackOverflow 上搜索了许多与 SignalR 连接相关的其他问题,但似乎都不适用于我的具体情况。
I have an application that uses a SignalR hub.我有一个使用 SignalR 集线器的应用程序。 A client can connect to the hub using 2 methods:
客户端可以使用 2 种方法连接到集线器:
The issue I'm having is with connection using the .NET Core API (method 1).我遇到的问题是使用 .NET Core API(方法 1)进行连接。 When the server-side application has been running for a significant amount of time (maybe 2 weeks), the SignalR connection that the API uses fails.
当服务器端应用程序运行很长时间(可能是 2 周)时,API 使用的 SignalR 连接会失败。 Direct connection to the SignalR hub (method 2) continues to work.
直接连接到 SignalR 集线器(方法 2)继续工作。
Here's how connection works via the API:以下是通过 API 进行连接的方式:
.NET Core Web API .NET 核心 Web API
[Route("~/api/heartbeat")]
[HttpPost]
public async Task SendHeartbeat(nodeId) {
await SignalRClient.SendHeartbeat(nodeId);
...
}
SignalRClient信号客户端
public static class SignalRClient
{
private static HubConnection _hubConnection;
/// <summary>
/// Static SignalRHub client - to ensure that a single connection to the SignalRHub is re-used,
/// and to prevent excessive connections that cause SignalR to fail
/// </summary>
static SignalRClient()
{
string signalRHubUrl = "...someUrl";
_hubConnection = new HubConnectionBuilder()
.WithUrl(signalRHubUrl)
.Build();
_hubConnection.Closed += async (error) =>
{
Log.Error("SignalR hub connection was closed - reconnecting. Error message - " + error.Message);
await Task.Delay(new Random().Next(0, 5) * 1000);
try
{
Log.Error("About to reconnect");
await _hubConnection.StartAsync();
Log.Error("Reconnect now requested");
}
catch (Exception ex)
{
Log.Error("Failed to restart connection to SignalR hub, following a disconnection: " + ex.Message);
}
};
InitializeConnection();
}
private static async void InitializeConnection()
{
try
{
Log.Information("Checking hub connection status");
if (_hubConnection.State == HubConnectionState.Disconnected)
{
Log.Information($"Starting SignalRClient using signalRHubUrl");
await _hubConnection.StartAsync();
Log.Information("SignalRClient started successfully");
}
}
catch (Exception ex)
{
Log.Error("Failed to start connection to SignalRClient : " + ex.Message + ", " + ex.InnerException.Message);
}
}
public static async Task SendHeartbeat(string nodeId)
{
try
{
Log.Information("Attempting to send heartbeat to SignalRHub");
await _hubConnection.InvokeAsync("SendNodeHeartbeatToMonitors", nodeId);
}
catch (Exception ex)
{
Log.Error($"Error when sending heartbeat to SignalRClient for NodeId: {nodeId}. Error: {ex.Message}");
}
}
After uptime of about 2 weeks, the connection fails and doesn't recover, I can see an error in the log:正常运行大约 2 周后,连接失败并且无法恢复,我可以在日志中看到错误:
Error when sending transaction to SignalRClient from /api/heartbeat: The 'InvokeCoreAsync' method cannot be called if the connection is not active
I don't understand how this is happening, as I'm using the _hubConnection.Closed
method in the SignalRClient
to handle the case when a connection is closed, which then executes await _hubConnection.StartAsync();
我不明白这是怎么发生的,因为我使用
SignalRClient
中的_hubConnection.Closed
方法来处理连接关闭时的情况,然后执行await _hubConnection.StartAsync();
to restart the connection, as shown in the code above.重新启动连接,如上面的代码所示。
The connection is regularly being closed for some reason (every 30mins), but it usually recovers the connection, and I see the following error in the log:由于某种原因,连接经常被关闭(每 30 分钟一次),但它通常会恢复连接,并且我在日志中看到以下错误:
SignalR hub connection was closed - reconnecting. Error message - The remote party closed the WebSocket connection without completing the close handshake.
This shows that the code is successfully entering the _hubConnection.Closed
method (as this is where I log that message), so it appear that the connection is usually restarted successfully.这表明代码已成功进入
_hubConnection.Closed
方法(因为这是我记录该消息的地方),因此看起来连接通常会成功重新启动。
So, why does the connection sometimes fail completely but then fail to be restarted?那么,为什么有时连接完全失败,然后又无法重新启动呢? I'm wondering if I'm connecting to the SignalR hub in a sensible way (in particularly, I'm wondering if using a static class for the
SignalRClient
is a good pattern).我想知道我是否以合理的方式连接到 SignalR 集线器(特别是,我想知道使用 static class 是否是
SignalRClient
的好模式)。 And I'm wondering if my actual problem is all of those The remote party closed the WebSocket connection without completing the close handshake.
而且我想知道我的实际问题是否都是那些
The remote party closed the WebSocket connection without completing the close handshake.
errors?错误? If that's the case, what could be causing those?
如果是这种情况,可能是什么原因造成的?
Any suggestions that point me in the right direction are greatly appreciated.非常感谢任何为我指明正确方向的建议。
I encountered this same problem a few years ago, which I solved at the time by placing all calls to StartAsync in their own task.几年前我遇到了同样的问题,当时我通过将所有对 StartAsync 的调用放在他们自己的任务中解决了这个问题。 And while I could be wrong about this, my own experiments indicated that the HubConnection itself isn't reusable, and thus also needs to be recreated after a disconnect.
虽然我可能对此有误,但我自己的实验表明 HubConnection 本身不可重用,因此在断开连接后也需要重新创建。
So essetentially I have an function called "CreateHubConnection" which does what you'd expect it to, and I have an async method to initiate server connections that looks like this:所以本质上我有一个名为“CreateHubConnection”的 function 可以满足您的期望,并且我有一个异步方法来启动服务器连接,如下所示:
private async Task ConnectToServer()
{
// keep trying until we manage to connect
while (true)
{
try
{
await CreateHubConnection();
await this.Connection.StartAsync();
return; // yay! connected
}
catch (Exception e) { /* bugger! */}
}
}
My initial connection runs this in a new task:我的初始连接在一个新任务中运行它:
this.Cancel = new CancellationTokenSource();
Task.Run(async () => await ConnectToServer(), this.Cancel.Token);
And the Connection.Closed handler also launches it in a new task: Connection.Closed 处理程序也在一个新任务中启动它:
this.Connection.Closed += async () =>
{
try
{
await Task.Delay(1000); // don't want to hammer the network
this.Cancel = new CancellationTokenSource();
await Task.Run(async () => await ConnectToServer(), this.Cancel.Token);
}
catch (Exception _e) { /* give up */ }
}
I don't know why this is necessary, but calling StartAsync directly from the Closed handler seems to create some kind of deadlock inside the SignalR library.我不知道为什么这是必要的,但是直接从 Closed 处理程序调用 StartAsync 似乎会在 SignalR 库中创建某种死锁。 I never did track down the exact cause for this.....it could have been because my original call to StartAsync was being called by the GUI thread.
我从来没有找到确切的原因......这可能是因为我最初对 StartAsync 的调用是由 GUI 线程调用的。 Putting connections in their own threads, creating new HubConnections each time, and disposing old HubConnections that were no longer needed fixed it.
将连接放在它们自己的线程中,每次都创建新的 HubConnections,并处理不再需要的旧 HubConnections 来修复它。
Would be very interested if someone with more knowledge of this has a better/easier solution.如果对此有更多了解的人有更好/更简单的解决方案,将会非常感兴趣。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.