简体   繁体   English

服务器端 SignalR 连接在正常运行时间较长后失败

[英]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 种方法连接到集线器:

  1. Via a .NET Core API that uses an underlying client to connect to the hub通过使用底层客户端连接到集线器的 .NET Core API
  2. Connecting directly to the URL of the hub直接连接集线器的URL

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.

相关问题 服务器端应用程序无法运行命令 - Server-side application fails to run command 是否可以将服务器端SignalR功能放在其他程序集中? - Is it possible to put the server-side SignalR functionality in a different assembly? 在 Blazor 服务器端上传文件时,有没有办法避免 SignalR - Is there a way to avoid SignalR when uploading files in Blazor Server-Side SignalR 集线器从服务器端关闭连接 - SignalR Hub close connection from server side 如何在服务器端捕获 ASP.NET Core 2 SignalR 异常并在客户端使用 JavaScript 处理它们? - How to catch ASP.NET Core 2 SignalR exceptions on server-side and handle them on client side with JavaScript? 使用ReactJS.NET的服务器端组件渲染失败 - Server-side rendering of component fails using ReactJS.NET 当使用具有 SignalR 服务器到客户端流式传输的 Channels 时,服务器端 Complete 是否保证传送到客户端? - When using Channels with SignalR server-to-client streaming, is the server-side Complete guaranteed to be delivered to the client? 在服务器端连接上验证 AD 用户 PrincipalContext - Verify an AD users PrincipalContext on server-side connection 如何使用带有.NET Core的SignalR的Hub类的服务器端Timer? - How to use server-side Timer from Hub class using SignalR with .NET Core? 如何在服务器端获取signalR客户端的连接ID? - How to obtain connection ID of signalR client on the server side?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM