我如何确定为什么 Microsoft.Owin returns HTTP 500 中的 SignalR?

How do I determine why SignalR in Microsoft.Owin returns HTTP 500?

我们已经为我们的后台守护进程构建了一个框架,以帮助它们保持一致的行为,因此我们不必继续重写相同的代码。

当然,十年后我们建造了一个新的。

旧框架中的应用程序之一是 SignalR 信号应用程序。它在 Microsoft 的 Owin 中托管一个 SignalR 实例,然后定期检查数据库是否需要推送任何内容,然后调用 SignalR 网站,然后该网站将使用配置的背板与任何其他网络服务器通信 运行,这然后将推送到各种网络客户端。

有点复杂,但它有效。

现在我的工作是将它移至新的后台应用程序框架,当我尝试启动网络应用程序时遇到 HTTP 500 错误。

新守护程序中的代码与旧应用程序中的代码几乎相同,我不明白为什么它在一个程序中工作而在另一个程序中不工作。

这两个框架的工作方式是每个守护进程都有一个重复调用的 doWork() 函数。在旧应用程序中,有一个 busy/wait 循环会调用 doWork()。在新的应用程序中,从 System.Timers.Timer ElapsedEventHandler.

调用 doWork()

我们定义了一个 class 来包装 SignalR 实例:

public class SignalRWebApp : IDisposable
{
    public readonly string signalRUrl;
    private IDisposable webApp;

    public SignalRWebApp()
    {
        this.signalRUrl = String.Format("http://localhost:{0}", getFreePort());
        this.webApp = null;
    }

    private static int getFreePort()
    {
        var listener = new TcpListener(IPAddress.Loopback, 0);
        listener.Start();
        var port = ((IPEndPoint) listener.LocalEndpoint).Port;
        listener.Stop();
        return port;
    }

    public bool started { get { return this.webApp != null; } }
    public void start(string signalRBackplaneConnectionString)
    {
        if (this.webApp != null)
            return;

        Action<IAppBuilder> startAction = app =>
        {
            app.UseCors(CorsOptions.AllowAll);
            GlobalHost.DependencyResolver.UseSqlServer(signalRBackplaneConnectionString);
            app.MapSignalR();
        };

        this.webApp = WebApp.Start(this.signalRUrl, startAction);
    }

    #region IDisposable
    [...]
    #endregion
}

我们将其实例存储在静态成员中,以便 work() 函数可以访问它:

public class Utils
{
    public static SignalRWebApp signalRWebApp;
}

然后我们将我们的启动代码包装在一个 using() 中,以便它在应用程序 运行 时存在。注意 - 如果您查看上面的 SignalRWebApp class,您会注意到它在启动之前实际上并没有做任何事情。

using (Utils.signalRWebApp = new SignalRWebApp())
{
    // initialize and run the background app
    // (this will repeatedly call work() until shutdown is requested)
}

我们的工作功能然后通过启动 SignalRWebApp 开始,如果它还没有 运行:

public class JobLockDaemon
{
    private string signalRUrl;
    private IHubProxy ticketLockSignalRHubProxy;
    private HubConnection signalRConnection;

    public JobLockDaemon()
    {
        this.checkedSnapshot = false;
        this.signalRUrl = null;
        this.ticketLockSignalRHubProxy = null;
        this.signalRConnection = null;
    }

    public void doWork()
    {
        this.connectToSignalR();

        // go ahead and do something
    }

    private void connectToSignalR()
    {
        if (this.signalRUrl == null)
        {
            if (!Utils.signalRWebApp.started)
            {
                Utils.signalRWebApp
                    .start(this.signalRBackplaneConnectionString());
            }

            this.signalRUrl = Utils.signalRWebApp.signalRUrl;
        }

        if (this.ticketLockSignalRHubProxy == null)
        {
            this.signalRConnection = new HubConnection(this.signalRUrl);
            this.ticketLockSignalRHubProxy = this.signalRConnection.CreateHubProxy("TicketLockSignalRHub");

            this.signalRConnection.Start().Wait();
        }
    }
}

一切正常,在旧的守护程序框架中。但是在新版本中,我在 this.signalRConnection.Start():

上得到了一个例外
System.AggregateException
Message    "One or more errors occurred."    string
InnerException    {
    "StatusCode: 500,
    ReasonPhrase: 'Internal Server Error',
    Version: 1.1,
    Content: System.Net.Http.StreamContent,
    Headers:
    {
        Date: Mon, 30 Mar 2020 16:29:27 GMT
        Server: Microsoft-HTTPAPI/2.0
        Content-Length: 0
    }"
}
System.Exception {
    Microsoft.AspNet.SignalR.Client.HttpClientException
}

所以问题是,我该去哪里查找 SignalR 服务器抛出 500 的原因?

为什么这在旧框架中有效而不在新框架中有效?

我能看到的唯一结构差异是,在新框架中,Start() 方法是从 Timer ElapsedEventHandler 调用的。这会有所作为吗?


FWIW:我尝试按照此处的说明初始化信号器跟踪:

https://docs.microsoft.com/en-us/aspnet/signalr/overview/testing-and-debugging/enabling-signalr-tracing

使用有效的旧框架,我看到:

SignalR.SqlMessageBus Information: 0 : SignalR SQL objects installed SignalR.SqlMessageBus Verbose: 0 : Created DbCommand: CommandType=Text, CommandText=SELECT [PayloadId] FROM [SignalR].[Messages_0_Id], Parameters= SignalR.ScaleoutMessageBus Information: 0 : Stream(0) - Changed state from Initial to Open SignalR.SqlMessageBus Verbose: 0 : Stream 0 : SqlReceiver started, initial payload id=4188906 SignalR.SqlMessageBus Verbose: 0 : Stream 0 : Executing receive reader, initial payload ID parameter=4188906

SignalR.SqlMessageBus Verbose: 0 : Stream 0 : Starting SQL notification listener SignalR.SqlMessageBus Verbose: 0 : Stream 0 : SQL notification listener started

使用没有的新框架:

SignalR.SqlMessageBus Information: 0 : SignalR SQL objects installed SignalR.SqlMessageBus Verbose: 0 : Created DbCommand: CommandType=Text, CommandText=SELECT [PayloadId] FROM [SignalR].[Messages_0_Id], Parameters= SignalR.ScaleoutMessageBus Information: 0 : Stream(0) - Changed state from Initial to Open SignalR.SqlMessageBus Verbose: 0 : Stream 0 : SqlReceiver started, initial payload id=4188977 SignalR.SqlMessageBus Verbose: 0 : Stream 0 : Executing receive reader, initial payload ID parameter=4188977

SignalR.SqlMessageBus Verbose: 0 : Stream 0 : Starting SQL notification listener

换句话说,在新框架中,我们看到 "Starting SQL notification listener" 但我们从未看到 "SQL notification listener started"。

关于为什么它没有开始的任何想法?

或者我在哪里可以找到它没有启动的原因?

通常,当异步任务出错并且不正确时,我会看到 AggregatedException 弹出窗口 handled/unwrapped。看起来你正在做所有同步的事情,所以你有可能在某处调用异步吗?

除此之外,还可以解压缩聚合异常。这是一个例子:https://docs.microsoft.com/en-us/dotnet/api/system.aggregateexception.flatten?view=netframework-4.8. Or Flattening of AggregateExceptions for Processing.

您可能还对增加 SigR 日志记录的详细程度感兴趣:https://docs.microsoft.com/en-us/aspnet/core/signalr/diagnostics?view=aspnetcore-3.1

希望对您有所帮助!

我仍然不知道如何获取有关 SignalR 内部到底发生了什么的真实信息。

但是我已经解决了我的问题。

我的启动代码因此创建了一个集线器代理:

this.ticketLockSignalRHubProxy = this.signalRConnection.CreateHubProxy("TicketLockSignalRHub");

我的问题是 TicketLockSignalRHub class 有两个实例,一个在启动程序集中,另一个在包含 KtOverseer.doMain() 方法的程序集中。如果我包含对 KtOverseer.doMain() 的调用,即使我没有调用它,我也会加载第二个程序集,SignalR 会看到两个 TicketLockSignalRHub classes,并引发异常。

删除或重命名其中一个集线器 classes 解决了问题,