2016-09-23 4 views
3

У меня есть служба TopShelf, которая использует асинхронный код для подключения к веб-службам и другим серверам приложений.Как обрабатывать ошибки async Start() в TopShelf

Если он не может инициализировать свои соединения при запуске, служба должна регистрировать некоторые ошибки и изящно останавливаться.

Я смотрел на this question об остановке TopShelf, когда условия запуска не выполняются. 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(); 
    } 
} 

Это, кажется, работает хорошо. Но я использую ключевое слово ожидания в нескольких местах в функции. Поэтому я не могу просто изменить свой метод Start(), чтобы взять HostControl и вернуть bool, потому что мне нужно было бы вернуть Task<bool> из метода async.

В настоящее время я разрешаю исключениям из функции Start(), чтобы TopShelf мог их видеть и автоматически останавливать службу, когда исключение пузырится вверх. Тем не менее, исключения теперь полностью не обрабатываются моим кодом, и поэтому я получаю неприятные необработанные сообщения об ошибках исключения в различных журналах, которые я пишу. Который я бы предпочел заменить хорошим сообщением об ошибке и отключением чистой службы.

Итак, у меня есть два вопроса:

  1. Есть ли какие-либо проблемы с использованием async void Start() метод Topshelf?
  2. Есть ли способ сделать это так, чтобы, если Init() выбрасывает исключение, детали исключения изящно регистрируются, а затем служба останавливается, учитывая, что моя служба запускает код async?

ответ

5

Во-первых, 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"() метод; т. е. вы должны вернуться с этого метода, как только начнется ваша служба, не оставайтесь там, пока служба работает.Учитывая, что вы уже используете асинхронной-ЖДИТЕ, я бы, вероятно, вместо того, чтобы не позвонить Wait() в Start(), но сохранить Task вернулся из StartAsync(), а затем, когда 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() будет вернитесь к TopShelf, как только он попадет в первый await, но wil Я продолжаю выполнение. Если ваш метод Stop() вызывает _canceller.Cancel(), тогда ваш метод async Start() будет завершен в следующий раз, когда вызывается Poll().

Однако вышеупомянутое является более чистым, и вы должны иметь возможность ждать, пока последний Poll() не завершит выполнение, чего вы не делали раньше. Как вы заметили, вы также сможете обрабатывать исключения.

Редактировать Я также переместить Init() вызов в Start(), как указано выше.

+0

Как я могу убедиться, что '_serviceTask', возвращаемый' ExecuteAsync() ', фактически запускается, не ожидая его в методе' Start() '? Я не могу ждать его там, потому что для этого потребуется 'Start()' async. В тестовом коде с помощью метода 'Poll()', основанного на 'Task.Delay()', когда я вызываю '_serviceTask.Start()', 'InvalidOperationException' выдается с сообщением' Start 'может не вызываться на обещание в стиле. «Если я не жду или не запустил задачу, она, похоже, останется в состоянии« WaitingForActivation »? – Hydrargyrum

+0

Ничего, мой тестовый код сосать. ExecuteAsync() возвращает запущенную задачу, поэтому мне не нужно ничего делать, чтобы ее запустить. Я не уверен, почему статус Task был «WaitingForActivation», а не запущен, но задача действительно выполняется и в конечном итоге завершается. – Hydrargyrum

+0

Одна из проблем с этим, если ExecuteAsync выбрасывает исключение, это никогда не будет замечено? – Peter

Смежные вопросы