c# hand hangfire - Web Api - Fire and Forget





4 Answers

Las tareas de verdadero fuego y olvido pueden ser difíciles en asp.net, ya que a menudo pueden morir junto con la solicitud de la cual fueron creadas.

Si está utilizando 4.5.2+, puede usar QueueBackgroundWorkItem para ejecutar la tarea. Al registrar las tareas a través de este método, AppDomain intentará retrasar el cierre hasta que se hayan completado, pero aún puede haber casos en que se eliminen antes de que se completen. Probablemente, esto sea lo más sencillo de hacer, pero vale la pena leerlo para ver exactamente qué instancias pueden provocar la cancelación de trabajos.

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

Existe una herramienta llamada hangfire que utiliza un almacén persistente para garantizar que una tarea se haya completado y tenga una funcionalidad integrada de reintento y registro de errores. Esto es más para "tareas de fondo" pero se adapta al fuego y al olvido. Esto es relativamente fácil de configurar y ofrece una variedad de tiendas de respaldo, no puedo recordar los detalles exactos, pero algunos requieren una licencia y otros no (como MSSQL).

net core background

Tengo una acción de API web donde necesito ejecutar alguna tarea y olvidarme de esta tarea. Así es como mi método está organizado ahora:

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

La cosa es que, obviamente, se detiene en la línea de espera cuando está lista y solo así continúa el trabajo. Y necesito "disparar y olvidar" ¿Debo llamar a Task.Run () sin ningún asíncrono?




Nunca dispares y olvides, porque entonces no verás ningún error, lo que hace que la resolución de problemas sea muy complicada si algo sale mal (no se garantiza que el manejo del método de tareas realice su propia excepción, ya que la tarea no funciona). comenzar con éxito en primer lugar). A menos que realmente no le importe si la tarea hace algo o no, pero eso es bastante inusual (ya que, si realmente no le importó, ¿por qué ejecutar la tarea en primer lugar)? Por lo menos, crea tu tarea con una continuation :

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

Usted puede hacer esto más sofisticado según lo dicten sus necesidades.

En el caso específico de una API web, es posible que desee esperar a que finalicen sus tareas en segundo plano antes de completar su solicitud. Si no lo hace, dejará cosas en ejecución en segundo plano que pueden tergiversar la cantidad de carga que realmente puede tomar su servicio, o incluso dejar de trabajar por completo si los clientes envían demasiadas solicitudes y usted no hace nada para estrangularlos. Puede reunir tareas y emitir una await Task.WhenAll(...) al final logre eso; De esta manera, puede continuar haciendo un trabajo útil mientras sus tareas en segundo plano se van, pero no regresa hasta que todo haya terminado.




Estoy de acuerdo con los demás en que no debes simplemente olvidar tu llamada. Sin embargo, para responder a su pregunta, si elimina la espera de la línea Task.Run (), la llamada no se bloqueará como se muestra here

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



Para invocar un método de fuego y olvido de WebApi, usé el siguiente código para asegurarme de que devuelve una respuesta OK . En mi caso, el token de autorización de portador creado al iniciar sesión se almacena en una 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();
            }
        }
    }



Related