如何处理TopShelf中的async Start()错误

本文关键字:async Start 错误 中的 TopShelf 处理 何处理 | 更新日期: 2023-09-27 18:08:22

我有一个TopShelf服务,它使用异步代码连接到web服务和其他应用服务器。

如果在启动时无法初始化连接,服务应该记录一些错误并优雅地停止。

我已经看过这个关于在启动条件不满足时停止TopShelf的问题。这个答案讨论了使用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并返回bool,因为我必须从async方法返回Task<bool>

我目前允许异常从Start()函数冒泡,以便TopShelf可以看到它们并在异常冒泡时自动停止服务。然而,我的代码完全无法处理这些异常,因此,在我写入的各种日志中,我最终得到了令人讨厌的未处理异常错误消息。我更愿意用一个漂亮的错误信息和一个干净的服务关闭来代替。

我有两个问题:

  1. 使用async void Start()方法对TopShelf有任何问题吗?
  2. 是否有一种方法可以使它,如果Init()抛出一个异常,异常详细信息被优雅地记录,然后服务停止,假设我的服务运行async代码?

如何处理TopShelf中的async Start()错误

首先,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"()方法;也就是说,您应该在服务启动后立即从该方法返回,而不是在服务运行时留在那里。鉴于您已经使用async-await,我可能不会在Start()中调用Wait(),而是保存从StartAsync()返回的Task,然后当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()方法将在它到达第一个await后立即返回TopShelf,但将继续执行。如果你的Stop()方法调用_canceller.Cancel(),那么你的异步Start()方法将在下一次调用Poll()时终止。

然而,上面的代码更简洁,你必须等到最后一个Poll()完成执行,这是你以前没有的。您还可以处理异常,如您所提到的。

编辑我还将Init()调用移到Start()中,如上所述。