with tasks single services page net job ihostedservice asp application c# .net web-applications background

c# - tasks - Tarea de fondo en una aplicación web ASP



net core background task (7)

Soy bastante nuevo en C # y recientemente construí una pequeña aplicación web con .NET 4.0. Esta aplicación tiene 2 partes: una está diseñada para ejecutarse permanentemente y buscará continuamente datos de recursos dados en la web. El otro accede a esos datos previa solicitud para analizarlos. Estoy luchando con la primera parte.

Mi enfoque inicial fue configurar un objeto Timer que ejecutara una operación de búsqueda (cualquiera que sea la operación realmente no importa aquí) cada, digamos, 5 minutos. Definiría ese temporizador en Application_Start y lo dejaría en vivo después de eso.

Sin embargo, recientemente me di cuenta de que las aplicaciones se crean / destruyen en función de las solicitudes de los usuarios (según mi observación, parecen destruidas después de un tiempo de inactividad). Como consecuencia, mi actividad de fondo se detendrá / continuará fuera de mi control, donde me gustaría que se ejecute continuamente, sin ninguna interrupción.

Así que aquí viene mi pregunta : ¿eso se puede lograr en una aplicación web? ¿O necesito absolutamente un servicio separado de Windows para ese tipo de cosas?

Gracias de antemano por su valiosa ayuda!

Guillaume


Creo que puedes lograrlo utilizando un BackgroundWorker , pero yo preferiría sugerirte que busques un servicio.


El contexto de su aplicación vive mientras su Proceso de trabajo en IIS esté funcionando. En IIS hay algunos tiempos de espera predeterminados para cuando el proceso de trabajo se reciclará (por ejemplo, número de minutos inactivos (20) o intervalos regulares (1740).

Dicho esto, si ajusta esas configuraciones en IIS, debería poder tener las solicitudes en vivo, sin embargo, las otras respuestas de usar un Servicio también funcionarían, solo una cuestión de cómo desea implementarlas.


Cree una clase estática con un constructor, creando un evento de temporizador. Sin embargo, como mencionó Steve Sloka, IIS tiene un tiempo de espera que tendrá que manipular para mantener el sitio funcionando.

using System.Runtime.Remoting.Messaging; public static class Variables { static Variables() { m_wClass = new WorkerClass(); // creates and registers an event timer m_flushTimer = new System.Timers.Timer(1000); m_flushTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnFlushTimer); m_flushTimer.Start(); } private static void OnFlushTimer(object o, System.Timers.ElapsedEventArgs args) { // determine the frequency of your update if (System.DateTime.Now - m_timer1LastUpdateTime > new System.TimeSpan(0,1,0)) { // call your class to do the update m_wClass.DoMyThing(); m_timer1LastUpdateTime = System.DateTime.Now; } } private static readonly System.Timers.Timer m_flushTimer; private static System.DateTime m_timer1LastUpdateTime = System.DateTime.MinValue; private static readonly WorkerClass m_wClass; } public class WorkerClass { public delegate WorkerClass MyDelegate(); public void DoMyThing() { m_test = "Hi"; m_test2 = "Bye"; //create async call to do the work MyDelegate myDel = new MyDelegate(Execute); AsyncCallback cb = new AsyncCallback(CommandCallBack); IAsyncResult ar = myDel.BeginInvoke(cb, null); } private WorkerClass Execute() { //do my stuff in an async call m_test2 = "Later"; return this; } public void CommandCallBack(IAsyncResult ar) { // this is called when your task is complete AsyncResult asyncResult = (AsyncResult)ar; MyDelegate myDel = (MyDelegate)asyncResult.AsyncDelegate; WorkerClass command = myDel.EndInvoke(ar); // command is a reference to the original class that envoked the async call // m_test will equal "Hi" // m_test2 will equal "Later"; } private string m_test; private string m_test2; }


Al hacer esto en una aplicación web no es ideal ... es posible , dado que el sitio siempre está activo.

Aquí hay un ejemplo: estoy creando un elemento de caché en el archivo global.asax con vencimiento. Cuando caduca, se desencadena un evento. Puede recuperar sus datos o lo que sea en el evento OnRemove ().

Luego puede establecer una llamada a una página (preferiblemente una muy pequeña) que activará el código en Application_BeginRequest que agregará nuevamente el elemento Cache con una caducidad.

global.asax:

private const string VendorNotificationCacheKey = "VendorNotification"; private const int IntervalInMinutes = 60; //Expires after X minutes & runs tasks protected void Application_Start(object sender, EventArgs e) { //Set value in cache with expiration time CacheItemRemovedCallback callback = OnRemove; Context.Cache.Add(VendorNotificationCacheKey, DateTime.Now, null, DateTime.Now.AddMinutes(IntervalInMinutes), TimeSpan.Zero, CacheItemPriority.Normal, callback); } private void OnRemove(string key, object value, CacheItemRemovedReason reason) { SendVendorNotification(); //Need Access to HTTPContext so cache can be re-added, so let''s call a page. Application_BeginRequest will re-add the cache. var siteUrl = ConfigurationManager.AppSettings.Get("SiteUrl"); var client = new WebClient(); client.DownloadData(siteUrl + "default.aspx"); client.Dispose(); } private void SendVendorNotification() { //Do Tasks here } protected void Application_BeginRequest(object sender, EventArgs e) { //Re-add if it doesn''t exist if (HttpContext.Current.Request.Url.ToString().ToLower().Contains("default.aspx") && HttpContext.Current.Cache[VendorNotificationCacheKey] == null) { //ReAdd CacheItemRemovedCallback callback = OnRemove; Context.Cache.Add(VendorNotificationCacheKey, DateTime.Now, null, DateTime.Now.AddMinutes(IntervalInMinutes), TimeSpan.Zero, CacheItemPriority.Normal, callback); } }

Esto funciona bien, si su tarea programada es rápida. Si se trata de un proceso prolongado ... definitivamente debes mantenerlo fuera de tu aplicación web.

Siempre que la primera solicitud haya iniciado la aplicación ... esto seguirá disparando cada 60 minutos, incluso si no tiene visitantes en el sitio.


Recientemente realicé una función de carga de archivos para cargar archivos de Access a la base de datos (no es la mejor manera, sino solo una solución temporal para un problema a largo plazo). Lo resolví creando un hilo de fondo que se ejecutó a través de la función ProcessAccess , y se eliminó cuando se completó.

A menos que IIS tenga una configuración en la que mate un hilo después de un período de tiempo determinado, independientemente de la inactividad, debería poder crear un hilo que llame a una función que nunca termina. No use la recursión porque la cantidad de funciones abiertas eventualmente explotará en su cara, pero solo tiene un bucle de (;;) 5,000,000 veces para que se mantenga ocupado :)


Sugiero ponerlo en un servicio de Windows. Evitas todos los aros mencionados anteriormente, el más grande es que IIS se reinicia. Un servicio de Windows también tiene los siguientes beneficios:

  1. Se puede iniciar automáticamente cuando el servidor se inicia. Si está ejecutando IIS y su servidor se reinicia, debe esperar hasta que se realice una solicitud para comenzar su proceso.
  2. Puede colocar este proceso de obtención de datos en otra máquina si es necesario
  3. Si termina equilibrando la carga de su sitio web en varios servidores, accidentalmente puede tener múltiples procesos de obtención de datos que le causan problemas
  4. Más fácil de mantener el código por separado (principio de responsabilidad única). Es más fácil mantener el código si solo hace lo que debe hacer y no intenta engañar a IIS.