如何处理TopShelf中的异步Start()错误 [英] How to handle async Start() errors in TopShelf

查看:441
本文介绍了如何处理TopShelf中的异步Start()错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用异步代码连接到Web服务和其他应用程序服务器的TopShelf服务.

I have a TopShelf service that uses async code to connect to web services and other application servers.

如果无法在启动时初始化其连接,则该服务应记录一些错误并正常停止.

If it's unable to initialize its connections on startup, the service should log some errors and gracefully stop.

我看过这个问题关于在不满足启动条件时停止TopShelf的问题. 此答案讨论使用TopShelf HostControl停止服务.

I've had a look at this question about stopping TopShelf when the start conditions aren't met. This answer talks about using the TopShelf HostControl to stop the service.

但是,答案取决于ServiceConfigurator<T>.WhenStarted<T>(Func<T, HostControl, bool> start)方法.

However, that answer relies on the ServiceConfigurator<T>.WhenStarted<T>(Func<T, HostControl, bool> start) method.

我目前正在以标准方式配置TopShelf服务:

I am currently configuring the TopShelf service in the standard way:

x.Service<MyService>(s =>
{
    s.ConstructUsing(() => new MyService());
    s.WhenStarted(s => s.Start());
    s.WhenStopped(s => s.Stop());
});

但是我的服务的Start()方法实际上是async,定义如下:

However my service's Start() method is actually async, defined like this:

public async void Start()
{
    await Init();
    while (!_canceller.Token.IsCancellationRequested)
    {
        await Poll();
    }
}

这似乎工作正常.但是我在函数中的几个地方都使用了await关键字.因此,我不能简单地将Start()方法更改为HostControl并返回bool,因为我必须从async方法返回Task<bool>.

This seems to work fine. But I use the await keyword in several places in the function. So, I can't simply change my Start() method to take a HostControl and return a bool, because I would have to return Task<bool> from an async method.

我目前允许从Start()函数中冒泡,以便TopShelf可以看到它们,并在冒泡时自动停止服务.但是,然后我的代码完全无法处理这些异常,因此最终我在写入的各种日志中都收到了令人讨厌的未处理异常错误消息.我希望将其替换为一个不错的错误消息和一个干净的服务关闭.

I'm currently allowing exceptions to bubble up from the Start() function so that TopShelf can see them and automatically stop the service when the exception bubbles up. However, the exceptions are then totally unhandled by my code, and I therefore end up with nasty unhandled exception error messages in the various logs I write to. Which I would prefer to replace with a nice error message and a clean service shut-down.

所以,我有两个问题:

  1. 在TopShelf中使用async void Start()方法是否有问题?
  2. 是否有办法做到这一点,如果Init()引发异常,则在我的服务运行async代码的情况下,可以优雅地记录异常详细信息,然后服务停止?
  1. Is there any problem with using an async void Start() method for TopShelf?
  2. Is there a way to make it so that if Init() throws an exception, the exception details are gracefully logged and then the service stops, given that my service runs async code?

推荐答案

首先,async void几乎总是不正确的,除非在某些真正的一劳永逸"方案中.您要将其更改为async Task.

Firstly, async void is almost always incorrect, except in some truly fire-and-forget scenarios. You want to change that to async Task.

然后有时您只需要在同步和异步代码之间的边界处使用.Wait().在这种情况下,您可能想将当前的异步Start()方法重命名为StartAsync()并添加一个调用它的Start()方法:

Then sometimes you just have to use .Wait() at the border between sync and async code. In this case you probably want to rename your current async Start() method to StartAsync() and add a Start() method that calls it:

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(),留下类似这样的内容:

However, you have another issue, in that TopShelf's Start() method is not a "Run"() method; i.e. you are supposed to return from that method as soon as your service is started, not remain there while the service runs. Given you're already using async-await, I'd probably instead not call Wait() in Start(), but save the Task returned from StartAsync(), then when Stop() is called, signal your Task to stop using that existing _canceller, and only then in Stop() call .Wait(), leaving you with something like this:

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()时终止.

I should add that the way you had it, you probably kind-of get away things to an extent, in the sense that your async Start() method will return to TopShelf as soon as it hits the first await, but will continue executing. If your Stop() method calls _canceller.Cancel() then your async Start() method will terminate next time Poll() is called.

但是,上面的方法更干净,您必须等待直到最后一个Poll()执行完毕,您才可以执行此操作.如前所述,您还将能够处理异常.

However the above is cleaner, and you have to ability to wait until the last Poll() finishes executing, which you didn't before. You will also be able to handle exceptions, as you mention.

修改 如上所述,我还将Init()调用移到Start()中.

Edit I'd also move the Init() call into Start(), as above.

这篇关于如何处理TopShelf中的异步Start()错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆