gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 12:35:37 35 4
gpt4 key购买 nike

我在 StackOverflow 上搜索了许多与 SignalR 连接相关的其他问题,但似乎没有一个适用于我的具体情况。

我有一个使用 SignalR 集线器的应用程序。客户端可以使用两种方法连接到集线器:

  • 通过使用底层客户端连接到集线器的 .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
    我不明白这是怎么发生的,因为我正在使用 _hubConnection.Closed SignalRClient 中的方法处理连接关闭的情况,然后执行 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 使用静态类是否是一个好的模式)。我想知道我的实际问题是否是所有这些 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 库中创建某种死锁。我从来没有追查过这个的确切原因......这可能是因为 GUI 线程正在调用我对 StartAsync 的原始调用。将连接放在它们自己的线程中,每次创建新的 HubConnections,并处理不再需要的旧 HubConnections 修复它。
    如果对此有更多了解的人有更好/更简单的解决方案,将会非常感兴趣。

    关于c# - 服务器端 SignalR 连接在长时间正常运行后失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62187401/

    35 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com