服务器端 SignalR 连接在长时间正常运行后失败
Server-side SignalR connection fails after significant uptime
我在 Whosebug 上搜索了许多与 SignalR 连接相关的其他问题,但其中 none 似乎适用于我的具体案例。
我有一个使用 SignalR 集线器的应用程序。客户端可以使用 2 种方法连接到集线器:
- 通过使用底层客户端连接到集线器的 .NET Core API
- 直接连接到集线器的URL
我遇到的问题是使用 .NET Core API(方法 1)的连接。当服务器端应用程序 运行 很长一段时间(可能是 2 周)时,API 使用的 SignalR 连接失败。直接连接到 SignalR 集线器(方法 2)继续工作。
以下是通过 API 进行连接的方式:
.NET Core 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}");
}
}
正常运行大约 2 周后,连接失败并且没有恢复,我可以在日志中看到错误:
Error when sending transaction to SignalRClient from /api/heartbeat: The 'InvokeCoreAsync' method cannot be called if the connection is not active
我不明白这是怎么发生的,因为我在 SignalRClient
中使用 _hubConnection.Closed
方法来处理连接关闭的情况,然后执行 await _hubConnection.StartAsync();
重新开始连接,如上代码所示。
由于某种原因(每 30 分钟),连接 定期 关闭,但它通常会恢复连接,我在日志中看到以下错误:
SignalR hub connection was closed - reconnecting. Error message - The remote party closed the WebSocket connection without completing the close handshake.
这表明代码已成功进入 _hubConnection.Closed
方法(因为这是我记录该消息的地方),因此看起来连接通常已成功重新启动。
那么,为什么有时连接完全失败,但又无法重新启动呢?我想知道我是否以合理的方式连接到 SignalR 集线器(特别是,我想知道对 SignalRClient
使用静态 class 是否是一个好的模式)。我想知道我的实际问题是否是所有这些 The remote party closed the WebSocket connection without completing the close handshake.
错误?如果是这样,可能是什么原因造成的?
非常感谢为我指明正确方向的任何建议。
几年前我遇到了同样的问题,当时我通过将对 StartAsync 的所有调用放在他们自己的任务中来解决这个问题。虽然我对此可能是错误的,但我自己的实验表明 HubConnection 本身不可重用,因此在断开连接后也需要重新创建。
所以基本上我有一个名为“CreateHubConnection”的函数,它可以完成您所期望的功能,并且我有一个异步方法来启动服务器连接,如下所示:
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! */}
}
}
我的初始连接在新任务中运行:
this.Cancel = new CancellationTokenSource();
Task.Run(async () => await ConnectToServer(), this.Cancel.Token);
并且 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 */ }
}
我不知道为什么这是必要的,但直接从 Closed 处理程序调用 StartAsync 似乎会在 SignalR 库中造成某种死锁。我从来没有找到确切的原因......这可能是因为我对 StartAsync 的原始调用是由 GUI 线程调用的。将连接放在它们自己的线程中,每次都创建新的 HubConnections,并处理不再需要的旧 HubConnections 修复它。
如果对此有更多了解的人有 better/easier 解决方案,将会非常感兴趣。
我在 Whosebug 上搜索了许多与 SignalR 连接相关的其他问题,但其中 none 似乎适用于我的具体案例。
我有一个使用 SignalR 集线器的应用程序。客户端可以使用 2 种方法连接到集线器:
- 通过使用底层客户端连接到集线器的 .NET Core API
- 直接连接到集线器的URL
我遇到的问题是使用 .NET Core API(方法 1)的连接。当服务器端应用程序 运行 很长一段时间(可能是 2 周)时,API 使用的 SignalR 连接失败。直接连接到 SignalR 集线器(方法 2)继续工作。
以下是通过 API 进行连接的方式:
.NET Core 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}");
}
}
正常运行大约 2 周后,连接失败并且没有恢复,我可以在日志中看到错误:
Error when sending transaction to SignalRClient from /api/heartbeat: The 'InvokeCoreAsync' method cannot be called if the connection is not active
我不明白这是怎么发生的,因为我在 SignalRClient
中使用 _hubConnection.Closed
方法来处理连接关闭的情况,然后执行 await _hubConnection.StartAsync();
重新开始连接,如上代码所示。
由于某种原因(每 30 分钟),连接 定期 关闭,但它通常会恢复连接,我在日志中看到以下错误:
SignalR hub connection was closed - reconnecting. Error message - The remote party closed the WebSocket connection without completing the close handshake.
这表明代码已成功进入 _hubConnection.Closed
方法(因为这是我记录该消息的地方),因此看起来连接通常已成功重新启动。
那么,为什么有时连接完全失败,但又无法重新启动呢?我想知道我是否以合理的方式连接到 SignalR 集线器(特别是,我想知道对 SignalRClient
使用静态 class 是否是一个好的模式)。我想知道我的实际问题是否是所有这些 The remote party closed the WebSocket connection without completing the close handshake.
错误?如果是这样,可能是什么原因造成的?
非常感谢为我指明正确方向的任何建议。
几年前我遇到了同样的问题,当时我通过将对 StartAsync 的所有调用放在他们自己的任务中来解决这个问题。虽然我对此可能是错误的,但我自己的实验表明 HubConnection 本身不可重用,因此在断开连接后也需要重新创建。
所以基本上我有一个名为“CreateHubConnection”的函数,它可以完成您所期望的功能,并且我有一个异步方法来启动服务器连接,如下所示:
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! */}
}
}
我的初始连接在新任务中运行:
this.Cancel = new CancellationTokenSource();
Task.Run(async () => await ConnectToServer(), this.Cancel.Token);
并且 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 */ }
}
我不知道为什么这是必要的,但直接从 Closed 处理程序调用 StartAsync 似乎会在 SignalR 库中造成某种死锁。我从来没有找到确切的原因......这可能是因为我对 StartAsync 的原始调用是由 GUI 线程调用的。将连接放在它们自己的线程中,每次都创建新的 HubConnections,并处理不再需要的旧 HubConnections 修复它。
如果对此有更多了解的人有 better/easier 解决方案,将会非常感兴趣。