【问题标题】:How do I determine why SignalR in Microsoft.Owin returns HTTP 500?如何确定 Microsoft.Owin 中的 SignalR 为什么返回 HTTP 500?
【发布时间】:2020-04-01 23:49:56
【问题描述】:

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

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

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

有点复杂,但确实有效。

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

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

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

我们已经定义了一个类来包装 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
}

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

public class Utils
{
    public static SignalRWebApp signalRWebApp;
}

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

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();
        }
    }
}

在旧的 Daemon 框架中一切正常。但在新版本中,我在 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 信息:0:已安装 SignalR SQL 对象 SignalR.SqlMessageBus 详细:0:创建 DbCommand: CommandType=文本,CommandText=SELECT [PayloadId] FROM [SignalR].[Messages_0_Id],参数= SignalR.ScaleoutMessageBus 信息:0:Stream(0) - 将状态从初始更改为打开 SignalR.SqlMessageBus 详细:0:流 0:SqlReceiver 已启动, 初始负载 id=4188906 SignalR.SqlMessageBus 详细:0:流 0 : 执行接收阅读器,初始payload ID参数=4188906

SignalR.SqlMessageBus 详细:0:流 0:启动 SQL 通知侦听器 SignalR.SqlMessageBus 详细:0:流 0: SQL 通知侦听器已启动

使用没有的新框架:

SignalR.SqlMessageBus 信息:0:已安装 SignalR SQL 对象 SignalR.SqlMessageBus 详细:0:创建 DbCommand: CommandType=文本,CommandText=SELECT [PayloadId] FROM [SignalR].[Messages_0_Id],参数= SignalR.ScaleoutMessageBus 信息:0:Stream(0) - 将状态从初始更改为打开 SignalR.SqlMessageBus 详细:0:流 0:SqlReceiver 已启动, 初始负载 id=4188977 SignalR.SqlMessageBus 详细:0:流 0 : 执行接收阅读器,初始payload ID参数=4188977

SignalR.SqlMessageBus 详细:0:流 0:启动 SQL 通知监听器

换句话说,在新框架中,我们看到“正在启动 SQL 通知侦听器”,但我们从未看到“SQL 通知侦听器已启动”。

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

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

【问题讨论】:

    标签: c# .net signalr owin


    【解决方案1】:

    通常,当异步任务出错并且未正确处理/解包时,我会看到 AggregatedException 弹出窗口。看起来您正在同步所有内容,那么您是否有可能在某处调用异步?

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

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

    希望对你有帮助!

    【讨论】:

      【解决方案2】:

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

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

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

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

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

      删除或重命名其中一个中心类可以解决问题,

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-02-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-23
        • 1970-01-01
        相关资源
        最近更新 更多