我们为后台守护程序构建了一个框架,以帮助保持后台守护程序的一致行为,因此我们不必继续重写相同的代码。
当然,十年后,我们又建造了一个新的。
旧框架中的应用程序之一是SignalR信令应用程序。它在Microsoft的Owin中托管一个SignalR实例,然后定期检查数据库中需要推送的内容,然后调用SignalR网站,然后该网站将使用配置的背板与正在运行的任何其他Web服务器进行对话,然后将其推送出去各种Web客户端。
有点令人费解,但是有效。
现在将其移至新的后台应用程序框架是我的工作,当我尝试启动Web应用程序时遇到HTTP 500错误。
新守护程序中的代码与旧应用程序中的代码几乎相同,并且我看不到为什么它在一个应用程序中起作用而在另一个应用程序中不起作用。
两种框架的工作方式是,每个守护程序都有一个doWork()函数,该函数会被重复调用。在旧的应用程序中,有一个忙/等待循环,它将调用doWork()。在新应用中,从System.Timers.Timer ElapsedEventHandler中调用doWork()。
我们定义了一个包装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类,您会注意到,在开始之前,它实际上并不会做任何事情。
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?
为什么这在旧框架中而不在新框架中起作用?
我可以看到的唯一结构差异是,在新框架中,从Timer ElapsedEventHandler调用了Start()方法。会有所作为吗?
[通常,当异步任务出错并且未正确处理/展开时,我会看到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
希望有帮助!