it-swarm.com.ru

Web Api - Огонь и Забудь

У меня есть действие Web API, в котором мне нужно выполнить какую-то задачу и забыть об этой задаче .... Вот как теперь организован мой метод:

public async Task<SomeType> DoSth()
{
    await Task.Run(...);
    .....
    //Do some other work
}

Дело в том, что, очевидно, он останавливается на линии ожидания, ожидая, когда это будет сделано, и только затем продолжает работу. И мне нужно «запустить и забыть» Должен ли я просто вызвать Task.Run () без каких-либо асинхронных ожиданий?

21
D. Jones

И мне нужно "выстрелить и забыть"

У меня есть запись в блоге, в которой подробно рассматриваются несколько разных подходов к fire-and-Forgot на ASP.NET .

В заключение: во-первых, постарайтесь вообще не делать «забей и забудь». Это почти всегда плохая идея. Вы действительно хотите "забыть"? Как в, не волнует, завершится ли он успешно или нет? Игнорировать любые ошибки? Принимать случайные "потерянные работы" без каких-либо уведомлений журнала? Почти всегда ответ «нет», «огненно-забывчивый» подход не подходит.

Надежным решением является создание правильной распределенной архитектуры. Таким образом, создайте сообщение, которое представляет выполняемую работу, и поместите это сообщение в надежную очередь (например, очередь Azure, MSMQ и т.д.). Затем создайте независимую серверную часть, которая обрабатывает эту очередь (например, Azure WebJob, служба Win32 и т.д.).

Должен ли я просто вызвать Task.Run () без какого-либо асинхронного ожидания?

Нет. Это худшее из возможных решений. Если вы должны делать «забей и забывай» и не хотите создавать распределенную архитектуру, подумайте о Hangfire. Если это не работает для вас, то по крайней мере очень вы должны зарегистрировать фоновую работу своего ковбоя в среде выполнения ASP.NET через HostingEnvironment.QueueBackgroundWorkItem или мою библиотеку фоновых задач ASP.NET . Обратите внимание, что QBWI и AspNetBackgroundTasks являются ненадежными решениями; они просто минимизируют вероятность того, что вы потеряете работу, а не предотвратить это.

20
Stephen Cleary

Задачи истинного запуска и забывания могут быть трудными в asp.net, поскольку они часто могут умереть вместе с запросом, частью которого они были созданы. 

Если вы используете 4.5.2+, то вы можете использовать QueueBackgroundWorkItem для запуска задачи. Регистрируя задачи с помощью этого метода, AppDomain будет пытаться отложить завершение работы до тех пор, пока все они не будут завершены, но могут быть случаи, когда они будут убиты до их завершения. Вероятно, это самая простая вещь, которую стоит сделать, но которую стоит прочитать, чтобы точно определить, какие экземпляры могут привести к отмене заданий.

HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
{
  await Task.Run(...);
});

Существует инструмент под названием hangfire , который использует постоянное хранилище, чтобы гарантировать, что задача завершена, и имеет встроенную функцию повтора и записи ошибок. Это больше для «фоновых задач», но подходит для огня и забывает. Это относительно легко настроить и предлагает множество вспомогательных хранилищ, я не могу вспомнить точные детали, но некоторые требуют лицензии, а некоторые нет (например, MSSQL).

5
Fermin

Для огня и забудьте, используйте это

Task.Factory.StartNew(async () =>
{
    using (HttpClient client = new HttpClient())
    {
        await client.PostAsync("http://localhost/api/action", new StringContent(""));
    }
});
4
Catalin

Я использую HangFire .

Это лучше для меня.

Простой способ выполнения фоновой обработки в приложениях .NET и .NET Core. Служба Windows или отдельный процесс не требуются.

Поддержанный постоянным хранением. Открытый и бесплатный для коммерческого использования. 

3
Majid gharaei

Никогда не запускайте и не забывайте, потому что тогда вы не увидите никаких ошибок, что приводит к очень неловкому устранению неполадок, если что-то пойдет не так (если метод задачи выполняет свою собственную обработку исключений, работа не гарантируется, потому что задача может и не работать успешно начать в первую очередь). Если вы действительно не возражаете, если задача что-то делает или нет, но это довольно необычно (поскольку, если вам действительно все равно, зачем запускать задачу в первую очередь)? Как минимум, создайте свою задачу с продолжением :

Task.Run(...)
  .ContinueWith(t => 
    logException(t.Exception.GetBaseException()),
    TaskContinuationOptions.OnlyOnFaulted
  )
;

Вы можете сделать это более сложным, как диктуют ваши потребности.

В конкретном случае веб-API вы, возможно, захотите дождаться завершения фоновых задач, прежде чем выполнить свой запрос. Если вы этого не сделаете, вы оставляете вещи, работающие в фоновом режиме, которые могут неверно представлять, какую нагрузку реально может принять ваша служба, или даже вообще перестать работать, если клиенты запускают слишком много запросов и вы ничего не делаете для их регулирования. Вы можете собрать задачи и выполнить await Task.WhenAll(...) в конце, чтобы достичь этого; таким образом, вы можете продолжать выполнять полезную работу, пока ваши фоновые задачи отпадают, но вы не вернетесь, пока все не будет сделано.

2
Jeroen Mostert

Я согласен с другими, что вы не должны просто забыть о своем звонке. Однако, чтобы ответить на ваш вопрос, если вы удалите await из строки Task.Run (), вызов не будет блокироваться, как показано здесь

public async Task<SomeType> DoSth()
{
    Task.Run(...);
    .....
    //Do some other work while Task.Run() continues in parallel.
}
0
Avinash Kumar

Здесь есть несколько постов, в которых говорится «Никогда не стреляй и не забывай», это неправильно и т.д.

Правда в том, что с некоторых точек зрения это не так. Скорее неправильно описано в некоторых отношениях.

На самом деле это должно сказать, что «пользователь должен уметь запускать и забывать, но он все равно должен сообщать об этом разработчику/владельцу».

Хорошим примером является форма обратной связи на веб-сайте. Клиент заполняет форму и за кулисами отправляет электронное письмо владельцу сайта. Некоторые почтовые серверы обрабатывают отправку очень долго, поэтому такие пакеты, как Hangfire (что я и использовал), отправляют его другому процессу за пределами «потока» веб-сервера.

Да, это должно как-то сообщить разработчику/владельцу (и сохранить контактную информацию), если произойдет ошибка. Но это никоим образом не должно информировать потенциального контакта о проблеме. (если вы не хотите потерять потенциального клиента)

0
William Humphreys

Для вызова метода пожара и забывания WebApi я использовал следующий код , чтобы убедиться, что он возвращает ответ OK. В моем случае токен авторизации на предъявителя, созданный при входе в систему, хранится в cookie:

...
FireAndForget().Wait();
...

private async Task FireAndForget()
    {
        using (var httpClient = new HttpClient())
        {
            HttpCookie cookie = this.Request.Cookies["AuthCookieName"];
            var authToken = cookie["AuthTokenPropertyName"] as string;
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
            using (var response = await httpClient.GetAsync("http://localhost/api/FireAndForgetApiMethodName"))
            {
                //will throw an exception if not successful
                response.EnsureSuccessStatusCode();
            }
        }
    }
0
CAK2