ASP.NET Core 2.2 SignalR 缓冲调用而不是异步调用

ASP.NET Core 2.2 SignalR Buffering Calls Instead of Invoking Asynchronously

我正在编写一个 ASP.NET Core 2.2 C# Web 应用程序,它使用 SignalR 在 Web 浏览器中接收来自 JavaScript 的调用。在服务器端,我像这样初始化 SignalR:

    public static void ConfigureServices(IServiceCollection services)
    {
        ...

        // Use SignalR
        services.AddSignalR(o =>
        {
            o.EnableDetailedErrors = true;
        });
    }

    public static void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        ...

        // Route to SignalR hubs
        app.UseSignalR(routes =>
        {
            routes.MapHub<ClientProxySignalR>("/clienthub");
        });

        ...
    }

我的 SignalR Hub class 有这样的方法:

public class ClientProxySignalR : Hub
{
    ...

    public async Task<IEnumerable<TagDescriptor>> GetRealtimeTags(string project)
    {
        return await _requestRouter.GetRealtimeTags(project).ConfigureAwait(false);
    }

    ...
}

在客户端:

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

connection.start().then(function () {
    ...
    // Enable buttons & stuff so you can click
    ...
}

document.getElementById("tagGetter").addEventListener("click", function (event) {
    connection.invoke("GetRealtimeTags", "Project1").then(data => {
        ...
        // use data
        ...
    }
}

就目前而言,这一切都有效,而且确实是异步工作的。因此,如果我单击 "tagGetter" 按钮,它会在我的集线器上调用 "GetRealtimeTags" 方法,并在数据返回时调用 "then" 部分。这也是真的,如果这需要一段时间才能达到 运行,而我同时再次单击 "tagGetter" 按钮,它会再次调用 .invoke("GetRealtimeTags") ...最少 JavaScript.

但是...这就是问题所在。虽然第二次调用是在 JavaScript 中进行的,但在第一次调用完成之前,它不会触发我的 SignalR Hub class 中的相应方法。这与我对应该发生的事情的理解不符。我认为每次调用 SignalR 集线器方法返回服务器都会导致创建集线器的新实例 class 来处理调用。相反,第一个电话似乎阻止了第二个电话。

如果我在我的 JavaScript 代码中创建两个不同的连接,那么我可以同时对它们进行两个调用而不会阻塞另一个。但我知道这不是完成这项工作的正确方法。

所以我的问题是:在这种情况下我做错了什么?

有趣的问题。

我认为应该禁用该按钮并在第一次单击时显示加载图标。但也许您的 UI 允许一次加载多个项目。想一想我们可能有一个 X-Y 问题。

总之,回答你的问题:

您可以轻松处理此问题的一种方法是在数据准备就绪时将 "requesting" 数据的过程与 "getting and sending" 数据的过程分离给用户。

  • 不要等待 GetRealtimeTags 而是启动一个记录调用者连接 ID 的后台任务
  • Return GetRealtimeTags
  • 什么都没有
  • 在后台任务中准备好结果后,调用一个新的 RealtimeTagsReady 方法,该方法将调用 JavaScript 客户端,并使用之前保存的连接 ID

如果有帮助请告诉我。

这是 websockets 的设计,以确保消息以准确的顺序传递。

更多信息可以参考这里:https://hpbn.co/websocket/

引用:

The preceding example attempts to send application updates to the server, but only if the previous messages have been drained from the client’s buffer. Why bother with such checks? All WebSocket messages are delivered in the exact order in which they are queued by the client. As a result, a large backlog of queued messages, or even a single large message, will delay delivery of messages queued behind it—head-of-line blocking!

他们还提出了一个变通解决方案:

To work around this problem, the application can split large messages into smaller chunks, monitor the bufferedAmount value carefully to avoid head-of-line blocking, and even implement its own priority queue for pending messages instead of blindly queuing them all on the socket.