c# - hand - Web Api-Fire and Forget
hangfire core (8)
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?
Y necesito "disparar y olvidar"
Tengo una publicación en el blog que incluye detalles de varios enfoques diferentes para activar y desactivar en ASP.NET .
En resumen: primero, trate de no hacer fuego y olvidar en absoluto. Casi siempre es una mala idea. ¿De verdad quieres "olvidar"? Al igual que en, no importa si se completa con éxito o no? ¿Ignorar cualquier error? ¿Acepta ocasionalmente "trabajo perdido" sin notificaciones de registro? Casi siempre, la respuesta es no, disparar y olvidar no es el enfoque apropiado.
Una solución confiable es construir una arquitectura distribuida adecuada. Es decir, cree un mensaje que represente el trabajo a realizar y ponga ese mensaje en una cola confiable (por ejemplo, Azure Queue, MSMQ, etc.). Luego, tenga un backend independiente que procese esa cola (por ejemplo, Azure WebJob, servicio Win32, etc.).
¿Debo llamar a Task.Run () sin ningún async-await?
No. Esta es la peor solución posible. Si debe hacer fuego y olvidar, y no está dispuesto a construir una arquitectura distribuida, considere Hangfire. Si eso no funciona para usted, al menos debe registrar su trabajo de fondo de vaquero con el tiempo de ejecución de ASP.NET a través de HostingEnvironment.QueueBackgroundWorkItem
o mi biblioteca de tareas de fondo de ASP.NET . Tenga en cuenta que QBWI y AspNetBackgroundTasks son soluciones poco confiables; simplemente minimizan la posibilidad de perder el trabajo, no lo impiden .
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.
}
Hay algunos mensajes aquí que empujan el "Nunca disparar y olvidar", está mal, etc.
La verdad es que no está mal desde ciertos puntos de vista. Más bien descrito de alguna manera.
Lo que realmente debería decir es que "el usuario debería poder disparar y olvidar, pero aún así debería decirle al desarrollador / propietario".
Un buen ejemplo es un formulario de contacto en un sitio web. El cliente rellena el formulario y, entre bastidores, envía un correo electrónico al propietario del sitio. Algunos servidores de correo electrónico tardan mucho en procesar el envío, por lo que paquetes como Hangfire (que es lo que yo usaría) lo envían a otro proceso fuera del "hilo" del servidor web.
Sí, esto debería avisar al desarrollador / propietario de alguna manera (y mantener los datos de contacto) si se produce un error. Pero de ninguna manera esto debería informar al posible contacto sobre el problema. (a menos que quieras perder un cliente potencial)
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).
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.
Para el fuego y el olvido, usa este
Task.Factory.StartNew(async () =>
{
using (HttpClient client = new HttpClient())
{
await client.PostAsync("http://localhost/api/action", new StringContent(""));
}
});
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();
}
}
}
Yo uso HangFire .
Esto es lo mejor para mí.
Una forma fácil de realizar el procesamiento en segundo plano en aplicaciones .NET y .NET Core. No se requiere servicio de Windows o proceso separado.
Respaldado por el almacenamiento persistente. Abierto y gratuito para uso comercial.