¿Puedo usar subprocesos para realizar trabajos de larga duración en IIS?

85

En una aplicación ASP.Net, el usuario hace clic en un botón en la página web y luego crea una instancia de un objeto en el servidor a través del controlador de eventos y llama a un método en el objeto. El método pasa a un sistema externo para hacer cosas y esto puede llevar un tiempo. Entonces, lo que me gustaría hacer es ejecutar esa llamada al método en otro hilo para poder devolver el control al usuario con "Su solicitud ha sido enviada". Estoy razonablemente feliz de hacer esto como disparar y olvidar, aunque sería aún mejor si el usuario pudiera seguir sondeando el estado del objeto.

Lo que no sé es si IIS permite que mi hilo siga funcionando, incluso si la sesión del usuario expira. Imagínese, el usuario dispara el evento y nosotros instanciamos el objeto en el servidor y disparamos el método en un nuevo hilo. El usuario está satisfecho con el mensaje "Su solicitud ha sido enviada" y cierra su navegador. Eventualmente, esta sesión de usuarios expirará en IIS, pero es posible que el subproceso aún se esté ejecutando, funcionando. ¿IIS permitirá que el hilo siga ejecutándose o lo matará y eliminará el objeto una vez que expire la sesión del usuario?

EDITAR: De las respuestas y comentarios, entiendo que la mejor manera de hacer esto es mover el procesamiento de larga duración fuera de IIS. Aparte de todo lo demás, se trata del problema de reciclaje del dominio de aplicación. En la práctica, necesito que la versión 1 despegue en un tiempo limitado y tengo que trabajar dentro de un marco existente, por lo que me gustaría evitar la capa de servicio, de ahí el deseo de simplemente disparar el hilo dentro de IIS. En la práctica, la "ejecución prolongada" aquí solo será de unos minutos y la concurrencia en el sitio web será baja, por lo que debería estar bien. Pero, la próxima versión definitivamente necesitará dividirse en una capa de servicio separada.

Frans
fuente
1
¿Tiene más detalles sobre lo que debe procesarse y su entorno de alojamiento? Por ejemplo, ¿puede instalar un servicio?
senfo
Siempre puede usar los métodos de programación Async y Await (Visual Studio 2012+). Mucho más limpio que administrar los hilos usted mismo.
SausageFingers

Respuestas:

65

Puedes lograr lo que quieras, pero normalmente es una mala idea. Varios motores de blogs y CMS ASP.NET adoptan este enfoque, porque quieren que se puedan instalar en un sistema de alojamiento compartido y no depender de un servicio de Windows que deba instalarse. Por lo general, inician un subproceso de larga duración en Global.asax cuando se inicia la aplicación, y ese proceso de subproceso pone en cola las tareas.

Además de reducir los recursos disponibles para IIS / ASP.NET para procesar solicitudes, también tiene problemas con la eliminación del subproceso cuando se recicla AppDomain, y luego tiene que lidiar con la persistencia de la tarea mientras está en curso, como así como comenzar el trabajo de nuevo cuando el AppDomain vuelva a funcionar.

Tenga en cuenta que, en muchos casos, AppDomain se recicla automáticamente en un intervalo predeterminado, así como si actualiza el web.config, etc.

Si puede manejar la persistencia y los aspectos transaccionales de la eliminación de su hilo en cualquier momento, entonces puede evitar el reciclaje de AppDomain mediante algún proceso externo que realice una solicitud en su sitio en algún intervalo, de modo que si el sitio se recicla, usted están garantizados para que se reinicie automáticamente en X minutos.

Una vez más, esto suele ser una mala idea.

EDITAR: Aquí hay algunos ejemplos de esta técnica en acción:

Servidor de la comunidad: uso de servicios de Windows frente a subprocesos en segundo plano para ejecutar código en intervalos programados Creación de un subproceso en segundo plano cuando se inicia el sitio web

EDITAR (desde un futuro lejano): estos días usaría Hangfire .

Bryan Batchelder
fuente
1
Gracias, esto es revelador. El reciclaje es definitivamente un asesino y tomo de lo que estás diciendo que puedo hacer esto como algo difícil y rápido, pero es peligroso.
Frans
Sí, aquí verifique esto: professionalaspnet.com/archive/2007/09/02/… Y aquí hay un hilo sobre cómo COmmunity Server maneja lo mismo: dev.communityserver.com/forums/t/459942.aspx
Bryan Batchelder
En mi caso, pude decirle a IIS que nunca reciclara y que nunca dejara inactivo el sitio. Esto hizo que pudiera trabajar bajo esta suposición y que el sitio solo se reciclaría durante la implementación y cuando ocurriera el mantenimiento planificado. Como solo estaba haciendo un sitio de tipo administrador, esto funcionó perfecto para mí.
Brett
1
Es extraño que esto tenga tantos votos a favor. Que esto sea posible es una de las cosas más interesantes de ASP.NET: que todas las solicitudes se atienden en el mismo AppDomain junto con cualquier hilo en segundo plano que pueda crear. Ninguna de las razones dadas por las que esto es una "mala idea" me parece convincente. AppDomains puede caer, sí, pero eso no es exclusivo del entorno ASP.NET. Necesita jugar bien con su entorno de alojamiento (google para HostingEnvironment.RegisterObject).
Juan
3
.NET 4.5.2 ha agregado una nueva característica QueueBackgroundWorkItempara registrar fácilmente tareas en segundo plano, es posible que desee actualizar su respuesta nuevamente.
Scott Chamberlain
39

No estoy de acuerdo con la respuesta aceptada.

Usar un hilo de fondo (o una tarea, comenzada Task.Factory.StartNew) está bien en ASP.NET. Al igual que con todos los entornos de alojamiento, es posible que desee comprender y cooperar con las instalaciones que rigen el cierre.

En ASP.NET, puede registrar el trabajo que necesita detenerse correctamente al apagar utilizando el HostingEnvironment.RegisterObjectmétodo. Vea este artículo y los comentarios para una discusión.

(Como señala Gerard en su comentario, ahora también hay HostingEnvironment.QueueBackgroundWorkItemllamadas a RegisterObjectpara registrar un programador para que el elemento de fondo funcione. En general, el nuevo método es mejor ya que se basa en tareas).

En cuanto al tema general que a menudo escucha que es una mala idea, considere la alternativa de implementar un servicio de Windows (u otro tipo de aplicación de proceso adicional):

  • No más implementación trivial con implementación web
  • No se puede implementar únicamente en sitios web de Azure
  • Dependiendo de la naturaleza de la tarea en segundo plano, es probable que los procesos tengan que comunicarse. Eso significa que alguna forma de IPC o el servicio tendrá que acceder a una base de datos común.

Tenga en cuenta también que algunos escenarios avanzados pueden incluso necesitar que el subproceso en segundo plano se ejecute en el mismo espacio de direcciones que las solicitudes. Veo el hecho de que ASP.NET puede hacer esto como una gran ventaja que se ha hecho posible a través de .NET.

Juan
fuente
Esto es genial. Tengo una tarea que necesita unos ~ 30 segundos para completarse. Perfecto si solo necesita retrasar el grupo de aplicaciones el tiempo suficiente para hacer el trabajo.
Scuba Steve
1
Desde .Net Framework 4.5.2 también puede usar HostingEnvironment.QueueBackgroundWorkItem (Action <CancellationToken>)
Gerard Carbó
En mis observaciones, parece que IIS finalmente cierra la API web ASP.NET que estoy ejecutando y no la vuelve a iniciar hasta que ingresa una nueva solicitud. Por lo tanto, en el momento designado cuando se supone que mi hilo debe iniciar la aplicación, puede ni siquiera estar corriendo. ¿Me equivoco en esto?
David Sopko
7

No querrá utilizar un hilo del grupo de hilos de IIS para esta tarea porque dejaría ese hilo sin poder procesar solicitudes futuras. Podría buscar páginas asincrónicas en ASP.NET 2.0 , pero esa tampoco sería la respuesta correcta. En cambio, parece que se beneficiaría de buscar en Microsoft Message Queue Server . Básicamente, agregaría los detalles de la tarea a la cola y otro proceso en segundo plano (posiblemente un servicio de Windows) estaría a cargo de llevar a cabo esa tarea. Pero la conclusión es que el proceso en segundo plano está completamente aislado de IIS.

senfo
fuente
Ah, MSMQ, una de mis tecnologías favoritas a largo plazo. Gracias por la información y el enlace.
Frans
6

Sugeriría usar HangFire para tales requisitos. Es un buen motor de disparo y olvido que se ejecuta en segundo plano, admite una arquitectura diferente y es confiable porque está respaldado por almacenamiento de persistencia.

Vipul
fuente
5

Hay un buen hilo y código de muestra aquí: http://forums.asp.net/t/1534903.aspx?PageIndex=2

Incluso he jugado con la idea de llamar a una página de mantener vivo en mi sitio web desde el hilo para ayudar a mantener vivo el grupo de aplicaciones. Tenga en cuenta que si está utilizando este método, necesita un manejo de recuperación realmente bueno, porque la aplicación podría reciclarse en cualquier momento. Como muchos han mencionado, este no es el enfoque correcto si tiene acceso a otras opciones de servicio, pero para el alojamiento compartido, esta puede ser una de sus únicas opciones.

Para ayudar a mantener vivo el grupo de aplicaciones, puede realizar una solicitud a su propio sitio mientras se procesa el hilo. Esto puede ayudar a mantener vivo el grupo de aplicaciones si su proceso se ejecuta durante mucho tiempo.

string tempStr = GetUrlPageSource("http://www.mysite.com/keepalive.aspx");


    public static string GetUrlPageSource(string url)
    {
        string returnString = "";

        try
        {
            Uri uri = new Uri(url);
            if (uri.Scheme == Uri.UriSchemeHttp)
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
                CookieContainer cookieJar = new CookieContainer();

                req.CookieContainer = cookieJar;

                //set the request timeout to 60 seconds
                req.Timeout = 60000;
                req.UserAgent = "MyAgent";

                //we do not want to request a persistent connection
                req.KeepAlive = false;

                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                Stream stream = resp.GetResponseStream();
                StreamReader sr = new StreamReader(stream);

                returnString = sr.ReadToEnd();

                sr.Close();
                stream.Close();
                resp.Close();
            }
        }
        catch
        {
            returnString = "";
        }

        return returnString;
    }
Daniel
fuente
5

Comenzamos por este camino, y realmente funcionó bien cuando nuestra aplicación estaba en un servidor. Cuando queríamos escalar horizontalmente a varias máquinas (o usar múltiples w3wp en una web garen) tuvimos que reevaluar y ver cómo administrar una cola de trabajo, manejo de errores, reintentos y el complicado problema de bloquear correctamente para asegurar solo una El servidor recoge el siguiente elemento.

... nos dimos cuenta de que no estamos en el negocio de escribir motores de procesamiento en segundo plano, así que buscamos soluciones existentes y aterrizamos utilizando el increíble proyecto OSS hangfire

Sergey Odinokov ha creado una verdadera joya con la que es realmente fácil comenzar y le permite intercambiar el backend de cómo el trabajo se persiste y se pone en cola. Hangfire utiliza subprocesos en segundo plano, pero conserva los trabajos, gestiona los reintentos y le brinda visibilidad de la cola de trabajos. Por lo tanto, los trabajos de hangfire son sólidos y sobreviven a todos los caprichos de los dominios de aplicaciones que se reciclan, etc.

Su configuración básica utiliza el servidor SQL como almacenamiento, pero puede cambiarlo por Redis o MSMQ cuando sea el momento de escalar. También tiene una excelente interfaz de usuario para visualizar todos los trabajos y su estado, además de permitirle volver a ponerlos en cola.

Mi punto es que si bien es completamente posible hacer lo que quieras en un hilo en segundo plano, hay mucho trabajo para hacerlo escalable y robusto. Está bien para cargas de trabajo simples, pero cuando las cosas se vuelven más complejas, prefiero usar una biblioteca especialmente diseñada en lugar de realizar este esfuerzo.

Para obtener más perspectiva sobre las opciones disponibles, consulte el blog de Scott Hanselman que cubre algunas opciones para manejar trabajos en segundo plano en asp.net. (Le dio a Hangfire una brillante reseña)

También, como lo menciona John, vale la pena leer el blog de Phil Haack sobre por qué el enfoque es problemático y cómo detener el trabajo en el hilo cuando se descarga el dominio de aplicación.

Avner
fuente
2

¿Puede crear un servicio de Windows para realizar esa tarea? Luego, use la comunicación remota .NET desde el servidor web para llamar al servicio de Windows para realizar la acción. Si ese es el caso, eso es lo que haría.

Esto eliminaría la necesidad de retransmitir en IIS y limitaría parte de su potencia de procesamiento.

De lo contrario, obligaría al usuario a sentarse allí mientras se realiza el proceso. De esa manera, se asegurará de que IIS lo complete y no lo elimine.

David Basarab
fuente
2

Parece haber una forma compatible de alojar trabajos de larga duración en IIS. Los servicios de flujo de trabajo parecen diseñados para esto, especialmente junto con Windows Server AppFabric . El diseño permite el reciclaje del grupo de aplicaciones al admitir la persistencia automática y la reanudación del trabajo de larga duración.

Olly
fuente
2

Puede ejecutar tareas en segundo plano y se completarán incluso después de que finalice la solicitud. No permita que se lance una excepción no detectada . Normalmente, siempre desea lanzar sus excepciones. Si se lanza una excepción en un nuevo hilo, bloqueará el proceso de trabajo de IIS, w3wp.exe , porque ya no se encuentra en el contexto de la solicitud. Eso también eliminará cualquier otra tarea en segundo plano que tenga en ejecución, además de las sesiones respaldadas por la memoria en proceso, si las está utilizando. Esto sería difícil de diagnosticar, por lo que se desaconseja la práctica.

Timothy González
fuente
1

Simplemente cree un proceso sustituto para ejecutar las tareas asincrónicas; no tiene que ser un servicio de Windows (aunque ese es el enfoque más óptimo en la mayoría de los casos. MSMQ está muy por encima de la eliminación).

Matt Davison
fuente