【问题标题】:.NET Core SignalR, Server timeoute / Reconnect issue.NET Core SignalR,服务器超时/重新连接问题
【发布时间】:2018-12-13 12:52:47
【问题描述】:

我的 MVC 解决方案中编写了一个 SignalR 集线器,其中一个 Javascript 客户端从视图连接。

连接的目的是从服务器接收对墙板的更改。这必须几乎立即发生并且需要终生连接,因为网页在屏幕上运行,没有直接的 PC 访问。

到目前为止,SignalR 连接工作了几个小时才出现错误。

我得到的错误是

Error: Connection disconnected with error 'Error: Server timeout elapsed without receiving a message form the server.'.
Failed to load resource: net::ERR_CONNECTION_TIMED_OUT
Warning: Error from HTTP request. 0:
Error: Failed to complete negotiation with the server: Error
Error: Failed to start the connection: Error

Uncaught (in promise) Error
    at new HttpError (singlar.js:1436)
    at XMLHttpRequest.xhr.onerror (singalr.js:1583)

我的客户代码

 let connection = new signalR.HubConnectionBuilder()
    .withUrl("/wbHub")
    .configureLogging(signalR.LogLevel.Information)
    .build();

connection.start().then(function () {
    connection.invoke("GetAllWallboards").then(function (wallboard) {
        for (var i = 0; i < wallboard.length; i++) {
            displayWallboard(wallboard[i]);
        }
        startStreaming();
    })
})

connection.onclose(function () {
    connection.start().then(function () {
            startStreaming();
    })
})

function startStreaming() {
    connection.stream("StreamWallboards").subscribe({
        close: false,
        next: displayWallboard
    });
}

中心代码:

public class WallboardHub : Hub
{
    private readonly WallboardTicker _WallboardTicker;

    public WallboardHub(WallboardTicker wallboardTicker)
    {
        _WallboardTicker = wallboardTicker;
    }

    public IEnumerable<Wallboard> GetAllWallboards()
    {
        return _WallboardTicker.GetAllWallboards();
    }

    public ChannelReader<Wallboard> StreamWallboards()
    {
        return _WallboardTicker.StreamWallboards().AsChannelReader(10);
    }

    public override async Task OnConnectedAsync()
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception exception)
    {
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users");
        await base.OnDisconnectedAsync(exception);
    }
}

问题 1:我处理重新连接的方式是否正确?从错误中感觉.onclose 有效,但它只尝试一次?无论如何在显示错误之前尝试 x 分钟?

问题 2:重新加载网站使连接再次工作,是否有可能在 signalR 连接错误时刷新浏览器?

【问题讨论】:

    标签: .net-core asp.net-core-mvc signalr


    【解决方案1】:

    我有同样的问题(问题 1),我解决了这个问题:

    const connection = new SignalR.HubConnectionBuilder()
        .withUrl("/hub")
        .configureLogging(SignalR.LogLevel.Information)
        .build();
    
    connect(connection);
    
    async function connect(conn){
        conn.start().catch( e => {
            sleep(5000);
            console.log("Reconnecting Socket");
            connect(conn);  
        }
        )
    }
    
    connection.onclose(function (e) {
                connect(connection);
        });
    
      async function sleep(msec) {
      return new Promise(resolve => setTimeout(resolve, msec));
    }
    

    每 5 秒尝试重新连接一次,但我不知道这样做是否正确。

    【讨论】:

    • 为什么要等五秒钟,这样不会阻塞 UI?
    • conn.start() 是异步函数,所以在 catch 事件中等待 5 秒不会阻塞 UI,但没有特殊原因等待,只是为了避免同时出现大量请求。
    • 你应该等待 sleep(5000) 调用
    • @AndréHauptfleisch 这个答案是 JavaScript,不是 C#,所以 not Thread.Sleep(5000)
    • 我认为 sleep 函数的用法应该有点不同,即 sleep(5000).then(() => {...});在示例中,它实际上不会等待。
    【解决方案2】:

    带有相应 SignalR 版本的 ASP.NET Core 2.1(当前 LTS 版本)似乎没有一些可用的集成重新连接方法。 @Shidarg 的代码对我不起作用,它在不定式循环中调用 reconnect 方法使我的浏览器崩溃。我也更喜欢 C# 中的 async/await 语法,所以我更新了它:

    let reconnectWaitTime = 5000
    let paramStr = '?myCustomArg=true'
    let client = new signalR.HubConnectionBuilder()
        .withUrl("/overviewHub" + paramStr)
        .build();
    
    client.onclose(async () => {
        console.warn(`WS connection closed, try reconnecting with loop interval ${reconnectWaitTime}`)
        tryReconnect(client)
    })
    await tryReconnect(client)
    
    async function tryReconnect(client) {
        try {
            let started = await client.start()
            console.log('WS client connected!')
    
            // Here i'm initializing my services, e.g. fetch history of a chat when connection got established
    
            return started;
        } catch (e) {
            await new Promise(resolve => setTimeout(resolve, reconnectWaitTime));
            return await tryReconnect(client)
        }
    }
    

    但对于 ASP.NET Core 3,它们包含了重新连接方法:

    let client = new signalR.HubConnectionBuilder()
        .withUrl("/myHub")
        .withAutomaticReconnect()
        .configureLogging(signalR.LogLevel.Information)
        .build();
    

    默认情况下,它会尝试三次重新连接:第一次在 2 秒后,第二次在 10 秒后,最后一次大约 30 秒。这可以通过将区间作为数组参数传递来修改:

    .withAutomaticReconnect([5000, 1500, 50000, null])
    

    此示例在 5s、15s 和 50s 后重试。最后一个 null 参数告诉 SignalR 停止重试。更多信息可以在这里找到:https://www.jerriepelser.com/blog/automatic-reconnects-signalr/

    【讨论】:

      【解决方案3】:

      配置自动重新连接只需要调用 HubConnectionBuilder 上的 withAutomaticReconnect。以下是用于配置连接的 JavaScript 代码:

      connection = new signalR.HubConnectionBuilder()
      .withUrl("/publish-document-job-progress")
      .withAutomaticReconnect()
      .configureLogging(signalR.LogLevel.Information)
      .build();
      

      您可以通过将重试延迟数组传递给对 withAutomaticReconnect() 的调用来配置退避期。默认值为 [0, 2000, 10000, 30000, null]。空值告诉 SignalR 停止尝试。因此,例如,如果我希望它在 0、1 秒和 5 秒重试,我可以按如下方式配置我的 HubConnectionBuilder:

      connection = new signalR.HubConnectionBuilder()
      .withUrl("/publish-document-job-progress")
      .withAutomaticReconnect([0, 1000, 5000, null])
      .configureLogging(signalR.LogLevel.Information)
      .build();
      

      【讨论】:

      • 注意:这仅适用于 @microsoft/signalr >= 3.0.0 而不适用于 @aspnet/signalr 包。
      • 是的,微软从 .NET Core 3.0 开始就集成了 signalR
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-09
      • 1970-01-01
      • 1970-01-01
      • 2020-08-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多