我如何确定为什么 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:我尝试按照此处的说明初始化信号器跟踪:
使用有效的旧框架,我看到:
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 解决了问题,
我们已经为我们的后台守护进程构建了一个框架,以帮助它们保持一致的行为,因此我们不必继续重写相同的代码。
当然,十年后我们建造了一个新的。
旧框架中的应用程序之一是 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:我尝试按照此处的说明初始化信号器跟踪:
使用有效的旧框架,我看到:
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 解决了问题,