如何处理 TopShelf 中的异步 Start() 错误
How to handle async Start() errors in TopShelf
我有一个使用异步代码连接到 Web 服务和其他应用程序服务器的 TopShelf 服务。
如果无法在启动时初始化其连接,该服务应记录一些错误并正常停止。
我看过 this question about stopping TopShelf when the start conditions aren't met. This answer 关于使用 TopShelf HostControl 停止服务的讨论。
但是,该答案依赖于 ServiceConfigurator<T>.WhenStarted<T>(Func<T, HostControl, bool> start)
方法。
我目前正在以标准方式配置 TopShelf 服务:
x.Service<MyService>(s =>
{
s.ConstructUsing(() => new MyService());
s.WhenStarted(s => s.Start());
s.WhenStopped(s => s.Stop());
});
然而我的服务的 Start()
方法实际上是 async
,定义如下:
public async void Start()
{
await Init();
while (!_canceller.Token.IsCancellationRequested)
{
await Poll();
}
}
这似乎工作正常。但是我在函数的几个地方使用了 await 关键字。所以,我不能简单地更改我的 Start()
方法以采用 HostControl
和 return 和 bool
,因为我必须 return Task<bool>
来自 async
方法。
我目前允许从 Start() 函数中冒出异常,以便 TopShelf 可以看到它们并在异常冒出时自动停止服务。然而,这些异常随后完全没有被我的代码处理,因此我最终在我写入的各种日志中得到了令人讨厌的未处理异常错误消息。我更愿意用一个很好的错误消息和一个干净的服务关闭来替换它。
那么,我有两个问题:
- 使用 TopShelf 的
async void Start()
方法有什么问题吗?
- 如果我的服务运行
async
代码,有没有办法让它在 Init()
抛出异常时正常记录异常详细信息,然后服务停止?
首先,async void
几乎总是不正确的,除了在一些真正的即发即弃的情况下。您想将其更改为 async Task
.
然后有时你只需要在同步和异步代码之间的边界使用.Wait()
。在这种情况下,您可能希望将当前的异步 Start()
方法重命名为 StartAsync()
并添加调用它的 Start()
方法:
public void Start()
{
StartAsync().Wait();
}
public async Task StartAsync()
{
await Init();
while (!_canceller.Token.IsCancellationRequested)
{
await Poll();
}
}
但是,您还有另一个问题,即 TopShelf 的 Start()
方法不是 "Run"()
方法;也就是说,您应该在服务启动后立即从该方法 return,而不是在服务运行时停留在那里。鉴于您已经在使用异步等待,我可能不会在 Start()
中调用 Wait()
,而是保存 StartAsync()
中的 Task
returned,然后当 Stop()
被调用时,通知你的 Task
停止使用现有的 _canceller
,并且 只有在 Stop()
调用 .Wait()
,给你留下这样的东西:
private Task _serviceTask;
public void Start()
{
Init().Wait();
_serviceTask = ExecuteAsync();
}
public void Stop()
{
_canceller.Cancel();
_serviceTask.Wait();
}
public async Task ExecuteAsync()
{
while (!_canceller.Token.IsCancellationRequested)
{
await Poll();
}
}
我应该补充一点,按照你的方式,你可能在某种程度上逃避了一些事情,因为你的异步 Start()
方法将 return 尽快到 TopShelf它命中第一个 await
,但会继续执行。如果您的 Stop()
方法调用 _canceller.Cancel()
,那么您的异步 Start()
方法将在下次调用 Poll()
时终止。
但是上面的更清晰,你必须等到最后一个 Poll()
完成执行,而你以前没有。正如您提到的,您还将能够处理异常。
编辑
如上所述,我还将 Init()
调用移至 Start()
。
我有一个使用异步代码连接到 Web 服务和其他应用程序服务器的 TopShelf 服务。
如果无法在启动时初始化其连接,该服务应记录一些错误并正常停止。
我看过 this question about stopping TopShelf when the start conditions aren't met. This answer 关于使用 TopShelf HostControl 停止服务的讨论。
但是,该答案依赖于 ServiceConfigurator<T>.WhenStarted<T>(Func<T, HostControl, bool> start)
方法。
我目前正在以标准方式配置 TopShelf 服务:
x.Service<MyService>(s =>
{
s.ConstructUsing(() => new MyService());
s.WhenStarted(s => s.Start());
s.WhenStopped(s => s.Stop());
});
然而我的服务的 Start()
方法实际上是 async
,定义如下:
public async void Start()
{
await Init();
while (!_canceller.Token.IsCancellationRequested)
{
await Poll();
}
}
这似乎工作正常。但是我在函数的几个地方使用了 await 关键字。所以,我不能简单地更改我的 Start()
方法以采用 HostControl
和 return 和 bool
,因为我必须 return Task<bool>
来自 async
方法。
我目前允许从 Start() 函数中冒出异常,以便 TopShelf 可以看到它们并在异常冒出时自动停止服务。然而,这些异常随后完全没有被我的代码处理,因此我最终在我写入的各种日志中得到了令人讨厌的未处理异常错误消息。我更愿意用一个很好的错误消息和一个干净的服务关闭来替换它。
那么,我有两个问题:
- 使用 TopShelf 的
async void Start()
方法有什么问题吗? - 如果我的服务运行
async
代码,有没有办法让它在Init()
抛出异常时正常记录异常详细信息,然后服务停止?
首先,async void
几乎总是不正确的,除了在一些真正的即发即弃的情况下。您想将其更改为 async Task
.
然后有时你只需要在同步和异步代码之间的边界使用.Wait()
。在这种情况下,您可能希望将当前的异步 Start()
方法重命名为 StartAsync()
并添加调用它的 Start()
方法:
public void Start()
{
StartAsync().Wait();
}
public async Task StartAsync()
{
await Init();
while (!_canceller.Token.IsCancellationRequested)
{
await Poll();
}
}
但是,您还有另一个问题,即 TopShelf 的 Start()
方法不是 "Run"()
方法;也就是说,您应该在服务启动后立即从该方法 return,而不是在服务运行时停留在那里。鉴于您已经在使用异步等待,我可能不会在 Start()
中调用 Wait()
,而是保存 StartAsync()
中的 Task
returned,然后当 Stop()
被调用时,通知你的 Task
停止使用现有的 _canceller
,并且 只有在 Stop()
调用 .Wait()
,给你留下这样的东西:
private Task _serviceTask;
public void Start()
{
Init().Wait();
_serviceTask = ExecuteAsync();
}
public void Stop()
{
_canceller.Cancel();
_serviceTask.Wait();
}
public async Task ExecuteAsync()
{
while (!_canceller.Token.IsCancellationRequested)
{
await Poll();
}
}
我应该补充一点,按照你的方式,你可能在某种程度上逃避了一些事情,因为你的异步 Start()
方法将 return 尽快到 TopShelf它命中第一个 await
,但会继续执行。如果您的 Stop()
方法调用 _canceller.Cancel()
,那么您的异步 Start()
方法将在下次调用 Poll()
时终止。
但是上面的更清晰,你必须等到最后一个 Poll()
完成执行,而你以前没有。正如您提到的,您还将能够处理异常。
编辑
如上所述,我还将 Init()
调用移至 Start()
。