Tareas ASP.NET de larga duración

Sé que hay un montón de API que hacen esto, pero también sé que el entorno de alojamiento (al ser ASP.NET) impone restricciones sobre lo que puedes hacer confiablemente en un hilo separado.

Podría estar completamente equivocado, así que por favor corrígeme si lo estoy, sin embargo, esto es lo que creo que sé.

  • Una solicitud generalmente expira después de 120 segundos (esto es configurable) pero finalmente el tiempo de ejecución de ASP.NET matará a una solicitud que tarda demasiado en completarse.
  • El entorno de alojamiento, generalmente IIS, emplea el reciclaje de procesos y puede en cualquier momento decidir reciclar su aplicación. Cuando esto sucede, todos los hilos se cancelan y la aplicación se reinicia. Sin embargo, no estoy seguro de qué tan agresivo sea, sería un poco estúpido suponer que abortaría una solicitud HTTP normal en curso, pero esperaría que abortara un hilo porque no sabe nada sobre la unidad de trabajo de un hilo.

Si tuviera que crear un modelo de progtwigción que de manera fácil, confiable y confiable coloque una tarea de ejecución larga, que debería ejecutarse durante días, ¿cómo podría lograr esto desde una aplicación ASP.NET?

Los siguientes son mis pensamientos sobre el tema:

He estado pensando mucho en alojar un servicio WCF en un servicio win32. Y habla con el servicio a través de WCF. Sin embargo, esto no es muy práctico, porque la única razón por la que elegiría hacerlo es enviar tareas (unidades de trabajo) desde varias aplicaciones web diferentes. Luego eventualmente le pedía al servicio actualizaciones de estado y actuaba en consecuencia. Mi mayor preocupación con esto es que NO sería una gran experiencia si tuviera que implementar cada tarea en el servicio para que pueda ejecutar algunas instrucciones. También está este tema de entrada, ¿cómo alimentaría este servicio con datos si tuviera un gran conjunto de datos y necesitara revisarlo?

Lo que normalmente hago en este momento es esto

SELECT TOP 10 * FROM WorkItem WITH (ROWLOCK, UPDLOCK, READPAST) WHERE WorkCompleted IS NULL 

Me permite usar una base de datos SQL Server como una cola de trabajo y sondear periódicamente la base de datos con esta consulta para el trabajo. Si el elemento de trabajo se completó con éxito, lo marqué como hecho y continúo hasta que no haya nada más que hacer. Lo que no me gusta es que teóricamente podría ser interrumpido en cualquier momento y si estoy entre el éxito y marcado como hecho, podría terminar procesando el mismo elemento de trabajo dos veces. Podría ser un poco paranoico y esto podría estar bien pero, según tengo entendido, no hay garantía de que eso no suceda …

Sé que ha habido preguntas similares en SO antes pero realmente no responde con una respuesta definitiva. Esto es algo muy común, sin embargo, el entorno de alojamiento ASP.NET está mal equipado para manejar trabajos de larga duración.

Por favor comparte tus pensamientos.

John,

Acepto que ASP.NET no es adecuado para tareas Async como las describió, ni debería serlo. Está diseñado como una plataforma de alojamiento web, no como un procesador para el hogar.

Hemos tenido situaciones similares en el pasado y hemos utilizado una solución similar a la que usted describió. En resumen, mantenga su servicio WCF bajo ASP.NET, utilice una tabla “Queue” con un servicio de Windows como “QueueProcessor”. El cliente debe sondear para ver si se realiza el trabajo (o usar mensajes para notificar al cliente).

Usamos una tabla que contenía el proceso y su información (por ejemplo, InvoicingRun). En esa tabla había un estado (Pendiente, En ejecución, Completado, Fallido). El cliente presentaría una nueva Facturación con un estado de Pendiente. Un servicio de Windows (el procesador) sondearía la base de datos para obtener cualquier ejecución que estuviera en la etapa pendiente (también podría usar la Notificación SQL para no tener que sondear. Si se encuentra una ejecución pendiente, la movería a la ejecución, hacer el procesamiento y luego moverlo a completado / fallido.

En el caso en que el proceso fallara fatalmente (por ejemplo, DB abajo, proceso cancelado), la ejecución quedaría en estado de ejecución y se requería intervención humana. Si el proceso falla en un estado no fatal (excepción, error), el proceso se moverá a error, y puede optar por volver a intentarlo o realizar una intervención humana.

Si hubo procesadores múltiples, el primero en moverlo a un estado en ejecución obtuvo ese trabajo. Puede usar este método para evitar que el trabajo se ejecute dos veces. Alternativo es hacer la selección y luego actualizar para ejecutar bajo una transacción. Asegúrese de que cualquiera de estos fuera de una transacción más grande de la transacción. SQL de muestra (aproximado):

 UPDATE InvoicingRun SET Status = 2 -- Running WHERE ID = 1 AND Status = 1 -- Pending IF @@RowCount = 0 SELECT Cast(0 as bit) ELSE SELECT Cast(1 as bit) 

Robar

Eche un vistazo a NServiceBus

NServiceBus es un marco de comunicaciones de código abierto para .NET con soporte en construcción para publicación / suscripción y procesos de larga ejecución.

Se trata de una tecnología basada en MSMQ, lo que significa que sus mensajes no se pierden ya que se conservan en el disco. Sin embargo, el Framework tiene un rendimiento impresionante y una API intuitiva.

¿Ha pensado en usar Workflow Foundation en lugar de su implementación personalizada? También le permite persistir estados. Las tareas se podrían definir como flujos de trabajo en este caso.

Solo algunos pensamientos …

Miguel

Use un marco simple de tareas / trabajos en segundo plano como Hangfire y aplique estos principios de mejores prácticas al diseño del rest de su solución:

  • Mantenga todas las acciones lo más pequeñas posible; para lograr esto, debes-
  • Divida trabajos de larga ejecución en lotes y hágalos cola (en una cola Hangfire o en un bus de otro tipo)
  • Asegúrese de que sus trabajos pequeños (partes de lotes de trabajos largos) sean idempotentes (tenga todo el contexto que necesitan para ejecutar en cualquier orden). De esta forma no es necesario usar un quete que mantenga una secuencia; porque entonces puedes
  • Paralelamente, la ejecución de trabajos en la cola depende de la cantidad de nodos que tenga en su granja de servidores web. Incluso puede controlar la cantidad de carga a la que está sujeta su granja (como una transacción para atender las solicitudes web). Esto garantiza que complete el trabajo completo (todos los lotes) de la manera más rápida y eficiente posible, sin comprometer su clúster al servicio de los clientes web.