ASP.NET MVC | Procesos "intensivos en el servidor"

436 views
Skip to first unread message

Leandro de los Santos

unread,
Apr 17, 2011, 8:29:50 AM4/17/11
to altnet-...@googlegroups.com

Hola,

les cuento que estoy haciendo una aplicación en ASP.NET MVC usando NHIbernate y Ninject (clásica aplicación con ABMS y procesos, nada espectacular).

 

Me llego el momento de hacer un proceso que barra la base y empiece a hacer cambios de estado, mandar mails, etc… En fin, el batch que usualmente tenes en las aplicaciones de negocio que debería correrse todas las noches.

 

La pregunta que tengo es, ¿Cómo implementan estos procesos?

 

Yo alguna vez hice uno en ASP.NET (sin MVC) y la forma más limpia que encontré como para no tener que preocuparme por los timeouts del servidor, etc. Fue

 

-          Una variable estática para saber que el proceso estaba corriendo (con pocos usuarios mucho problema no me daba, lo lógico hubiera sido meter algo en la DB para que funcionara correctamente pero alcanzo).

-          Lanzaba un thread con el proceso

-          Dentro del thread, ya por cuestiones de manejo de sesión hacia “paquetes” de datos a procesar y los ejecutaba, de esta forma mantenía un poco de control respecto a los volúmenes de datos que manejaba cada sesión de NH y aparte podía mandar los paquetes al ThreadPool y hacer mi proceso multithreading sin meterme en cosas raras (cada paquete a procesar tenía su sesión y corria en su thread, por lo tanto muerto el paquete, muerta la sesión de NH y sus datos).

-          Para que quede más bonito, si el proceso lo lanzaba un usuario, mediante un GUID que identificaba la ejecución del proceso, la aplicación cliente podía preguntar por el % de avance y actualizar un gauge.

 

La realidad es que esto lo arme así más que nada por gusto a complicarme, no era tanto el volumen de datos, al punto que el timeout del request no iba a ser un problema, pero es más fuerte que yo saber que hago las cosas “bien” (de esa forma me siento menos mal cuando a último momento termino poniendo algún clavito xD).

 

En fin, ¿alguien tiene otras recomendaciones acerca de cómo ejecutar procesos “pesados” en el servidor? ¿Llaman directamente al controller y que ejecute y no se preocupan por el timeout? ¿Tiran todo al threadpool y confían en que el proceso termine sin problema?

 

Aclaro: Esta aplicación se instalara en un hosting de los baratos… así que meter un servicio o tirar un exe es imposible, casi todos los schedules de procesos que vi en los hostings son “URLS” que invocan en determinados horarios, por lo que el proceso tiene que lanzarse desde una invocación a un action de un controller.

 

Saludos y gracias

Leandro

 

 

Jorge Fioranelli

unread,
Apr 17, 2011, 5:26:28 PM4/17/11
to altnet-...@googlegroups.com, <altnet-hispano@googlegroups.com>
Hola Leandro,

Como primer alternativa yo sugeriría un servicio windows que hostee wcf, pero por lo que decís del hosting esto no es una opción.

La opción de tener un thread único encargado de procesar esto no es mala, ya que no solo te aislas del timeout de asp.net sino que tampoco estas ocupando threads del pool.

Hay un par de cosas que yo le agregaría para hacer que la solución sea un poco mas robusta:

- mover esto a otro worker process, o sea sacarlo de la capa de presentación (controller) y poner el thread detrás de un servicio Wcf (aunque sea hosteado en asp.net)

- El servicio wcf colocaría el pedido de procesamiento en un repositorio intermedio persistente (msmq o db, no memoria), mientras que el thread corriendo en paralelo va chequeando el repositorio y procesando lo que haya pendiente.

- manejar transacciones y estados para que si el thread se muere, todo vuelva al estado original y el siguiente thread que se levante lo ejecute de nuevo.

- manejo para "mensajes envenenados" (poisoned messages).

- manejo de excepciones dentro del thread, ya que no burbujean.

- monitoreo del thread para asegurarte que siempre está corriendo y si no lo está auto lanzarlo de nuevo.

- muy buena instrumentación para saber que está pasando en todo momento (logs, perf counters, etc)

- evaluar si tiene realmente sentido usar un ORM en este escenario, ya tiene que ser un procesamiento bien rápido para evitar locks innecesarios en la db. Los viejos y queridos SPs suelen ser a veces una mejor alternativa para este tipo de trabajos.

- asegurarte que no hay otro sitios web corriendo en el mismo worker process y que el nadie haga iisreset en el servidor con cierta frecuencia (en algunos lados es una practica frecuente).

- evaluá si vas a necesitar escalar en el futuro, en caso afirmativo pensá en un modelo que soporte múltiples threads corriendo.


Si se me ocurre algo más te aviso.

Espero que te sirva. 


Un abrazo

JorgeF
@jorgefioranelli
--
Has recibido este mensaje porque estás suscrito al grupo "AltNet-Hispano" de Grupos de Google.
Para publicar una entrada en este grupo, envía un correo electrónico a altnet-...@googlegroups.com.
Para anular tu suscripción a este grupo, envía un correo electrónico a altnet-hispan...@googlegroups.com
Para tener acceso a más opciones, visita el grupo en http://groups.google.com/group/altnet-hispano?hl=es.

Nicolas Garfinkiel

unread,
Apr 17, 2011, 6:20:12 PM4/17/11
to altnet-...@googlegroups.com

Pregunta ingenua quizás, pero que clase de procesos tenes que correr para que sea necesario que hagas un scheduled batch process? Yo soy partidario que micro procesos son mejores que macro procesos mas largos e intensos. Y si se trata de liberar un request y no esperar a que un "micro proceso" termine, podes llamar a una acción dentro de otra acción usando la librería de httpclient de wcf starter kit. Con ella podes llamar a cualquier acción de controlador como su fuera un servicio restful, a modo fire and forget, y devolves el control de la aplicación mas rápido.

Así te olvidas de manejo de threads dentro de la aplicación web (no se si es muy recomendable manejar threads  propios en un entorno web) ya que delegas la ejecución micro de la operación como si fuera otro request web, y aprovechas la misma arquitectura mvc que estas usando para el resto de la app. Ah, y te permite correr tus procesos en cualquier servidor, tengas o no control full sobre el mismo.

Hay otros temas para considerar en este escenario, como temas de seguridad para transacciones, pero que son fácilmente manejables.

Ademas, creo que este escenario es bueno especialmente cuando tu app y db son utilizados intensivamente 24x7

Espero sus comentarios! 

Leandro de los Santos

unread,
Apr 18, 2011, 7:16:34 AM4/18/11
to altnet-...@googlegroups.com

Jorge, me doy idea de cómo implementar algunos puntos, pero me queda una duda con el más básico:

 

¿La idea de usar WCF es por separar conceptos o porque WCF te da algún beneficio en estos escenarios?

 

Gracias.

Leandro de los Santos

unread,
Apr 18, 2011, 7:17:40 AM4/18/11
to altnet-...@googlegroups.com

No,

la verdad que en esta aplicación tranquilamente puedo hacer correr el proceso nocturno en un request, en el peor de los casos partirlo en 2 o 3 procesos que corran en diferentes request.

 

La duda que tenía principalmente es saber qué alternativas hay para programar procesos “pesados” en web. En la solución que arme en su momento, el thread lo implemente por una cuestión simple, necesitaba liberar el request y devolver algo que identifique la corrida para posteriores consultas por el estado.

 

Vos propones anidar llamados a request del propio sitio partiendo el proceso. Es una idea interesante…

 

Saludos y gracias

 

 

From: altnet-...@googlegroups.com [mailto:altnet-...@googlegroups.com] On Behalf Of Nicolas Garfinkiel
Sent: Sunday, April 17, 2011 7:20 PM
To: altnet-...@googlegroups.com
Subject: Re: [altnet-hispano] ASP.NET MVC | Procesos "intensivos en el servidor"

 

Carlos Peix

unread,
Apr 18, 2011, 8:10:52 AM4/18/11
to altnet-hispano
Lo que yo he hecho con bastante exito es programar el proceso dentro de un request (webservice por ejemplo) y dipararlo desde afuera con una sencilla aplicacion de consola que lo unico que hace es llamar al servicio y salir (esperando o no esperando la respuesta).

Esa aplicacion se puede correr en el mismo servidor o en cualquier otro mientras tenga acceso HTTP al server principal.

Esto deja el codigo del proceso incorporado a la aplicacion principal con un servicio, lo cual es comodo en ciertos entornos donde instalar y actualizar mas de una aplicacion es mas complejo.

Las ideas que te da Jorge para robustizar el proceso son importantes.

Por ejemplo, hace poco hice un proceso que envia mailing a unos 400 o 500 destinatarios con informacion estadistica. No es un proceso muy pesado pero la comunicacion con el server SMTP lleva tiempo.

Hicimos un servicio (en nuestro caso una accion ASP.NET MVC) que envia 99 mails y termina.

Cuando empieza el proceso da de alta los 500 registros en una tabla y a medida que va enviando cada uno lo marca el registro como enviado. Cuando llega a 99, abandona el proceso.

Al dia siguiente se dispara de nuevo la aplicacion de consola y envia otros 99 mails y asi hasta que termina (tambien se podria disparar el proceso cada hora).

Si el proceso en el servidor se interrumpe (por ejemplo un reinicio de la aplicacion), en la corrida siguiente dejo desde el primer envio pendiente.

----------------------------------
Carlos Peix

2011/4/18 Leandro de los Santos <delossant...@gmail.com>

Jorge Fioranelli

unread,
Apr 18, 2011, 8:49:06 AM4/18/11
to altnet-...@googlegroups.com, <altnet-hispano@googlegroups.com>
La idea es que este corriendo en un proceso diferente para que cualquier problema en la UI no te tire abajo el proceso long running.

En mi opinion cuanto mas aislado esté menos problemas vas a tener.

Y si el proceso consume mucho CPU, se cuelga o necesita escalar, es mas difícil de solucionar si lo tenes integrado a la capa de presentación.

Saludos

JorgeF
@jorgefioranelli

cibrax

unread,
Apr 18, 2011, 2:45:46 PM4/18/11
to AltNet-Hispano
Si y acuerdencen que un fire & forget no es optimo para este scenario,
por que si bien el cliente tira el pedido y se libera. El servidor
igual queda procesando el trabajo y por ende toma un Thread que ya no
va a poder ser utilizado para servir mas requests hasta que se libere.
Es decir, un par de fire & forget con trabajos pesados y ya tomaste
todos los threads que el http server tenia para procesar nuevos
requests. Lo que dice Jorge tiene sentido, utilizar un unico thread
para procesar, y que la UI (en este caso MVC), encole trabajo para
este thread. En ASP.NET Forms existian Async Pages para resolver este
problema, pero ni idea si incluyeron algo similar en MVC.

Saludos
Pablo.

On Apr 18, 9:49 am, Jorge Fioranelli <jorge.fiorane...@gmail.com>
wrote:
> La idea es que este corriendo en un proceso diferente para que cualquier problema en la UI no te tire abajo el proceso long running.
>
> En mi opinion cuanto mas aislado esté menos problemas vas a tener.
>
> Y si el proceso consume mucho CPU, se cuelga o necesita escalar, es mas difícil de solucionar si lo tenes integrado a la capa de presentación.
>
> Saludos
>
> JorgeFhttp://blog.jorgef.net
> @jorgefioranelli
> > On 17/04/2011, at 10:29 PM, "Leandro de los Santos" <delossantos.lean...@gmail.com> wrote:
>
> > Hola,
>
> > les cuento que estoy haciendo una aplicación en ASP.NET MVC usando NHIbernate y Ninject (clásica aplicación con ABMS y procesos, nada espectacular).
>
> > Me llego el momento de hacer un proceso que barra la base y empiece a hacer cambios de estado, mandar mails, etc… En fin, el batch que usualmente tenes en las aplicaciones de negocio que debería correrse todas las noches.
>
> > La pregunta que tengo es, ¿Cómo implementan estos procesos?
>
> > Yo alguna vez hice uno en ASP.NET (sin MVC) y la forma más limpia que encontré como para no tener que preocuparme por los timeouts del servidor, etc. Fue
>
> > -          Una variable estática para saber que el proceso estaba corriendo (con pocos usuarios mucho problema no me daba, lo lógico hubiera sido meter algo en la DB para que funcionara correctamente pero alcanzo).
>
> > -          Lanzaba un thread con el proceso
>
> > -          Dentro del thread, ya por cuestiones de manejo de sesión hacia “paquetes” de datos a procesar y los ejecutaba, de esta forma mantenía un poco de control respecto a los volúmenes de datos que manejaba cada sesión de NH y aparte podía mandar los paquetes al ThreadPool y hacer mi proceso multithreading sin meterme en cosas raras (cada paquete a procesar tenía su sesión y corria en su thread, por lo tanto muerto el paquete, muerta la sesión de NH y sus datos).
>
> > -          Para que quede más bonito, si el proceso lo lanzaba un usuario, mediante un GUID que identificaba la ejecución del proceso, la aplicación cliente podía preguntar por el % de avance y actualizar un gauge.
>
> > La realidad es que esto lo arme así más que nada por gusto a complicarme, no era tanto el volumen de datos, al punto que el timeout del request no iba a ser un problema, pero es más fuerte que yo saber que hago las cosas “bien” (de esa forma me siento menos mal cuando a último momento termino poniendo algún clavito xD).
>
> > En fin, ¿alguien tiene otras recomendaciones acerca de cómo ejecutar procesos “pesados” en el servidor? ¿Llaman directamente al controller y que ejecute y no se preocupan por el timeout? ¿Tiran todo al threadpool y confían en que el proceso termine sin problema?
>
> > Aclaro: Esta aplicación se instalara en un hosting de los baratos… así que meter un servicio o tirar un exe es imposible, casi todos los schedules de procesos que vi en los hostings son “URLS” que invocan en determinados horarios, por lo que el proceso tiene que lanzarse desde una invocación a un action de un controller.
>
> > Saludos y gracias
>
> > Leandro
>
> > --
> > Has recibido este mensaje porque estás suscrito al grupo "AltNet-Hispano" de Grupos de Google.
> > Para publicar una entrada en este grupo, envía un correo electrónico a altnet-...@googlegroups.com.
> > Para anular tu suscripción a este grupo, envía un correo electrónico a altnet-hispan...@googlegroups.com
> > Para tener acceso a más opciones, visita el grupo enhttp://groups.google.com/group/altnet-hispano?hl=es.
>
> > --
> > Has recibido este mensaje porque estás suscrito al grupo "AltNet-Hispano" de Grupos de Google.
> > Para publicar una entrada en este grupo, envía un correo electrónico a altnet-...@googlegroups.com.
> > Para anular tu suscripción a este grupo, envía un correo electrónico a altnet-hispan...@googlegroups.com
> > Para tener acceso a más opciones, visita el grupo enhttp://groups.google.com/group/altnet-hispano?hl=es.
>
> > --
> > Has recibido este mensaje porque estás suscrito al grupo "AltNet-Hispano" de Grupos de Google.
> > Para publicar una entrada en este grupo, envía un correo electrónico a altnet-...@googlegroups.com.
> > Para anular tu suscripción a este grupo, envía un correo electrónico a altnet-hispan...@googlegroups.com
> > Para tener acceso a más opciones, visita el grupo enhttp://groups.google.com/group/altnet-hispano?hl=es.- Hide quoted text -
>
> - Show quoted text -

nelopa...@gmail.com

unread,
Apr 18, 2011, 3:10:45 PM4/18/11
to altnet-...@googlegroups.com
Hola Leandro ¿cómo andás?

se me ocurren dos enfoques posibles a partir de lo ya expresado en este hilo.

1. Un proceso robusto, tolerante a caidas, que marque registros a
procesar, procesados, etc... para esto me inclino mas por el servicio
de windows aunque podés lograr el mismo efecto agregando una líneas en
el Application_Start del Global.asax para chequear si hay procesos
pendiente.

2. Un proceso mas simple donde, por ejemplo, si se reinicia el AppPool
del IIS y se interrumpe el proceso, sea el usuario el responsable de
volver a lanzarlo. Para este último caso creo que te sería útil el
siguiente ejemplo que hice hace un tiempo en base a una importación de
datos que desarrolle en una aplicación web:
http://nelopauselli.blogspot.com/2010/09/proceso-asincronico-en-aspnet-mvc-con.html
este proceso funciona hoy en día en producción y según tengo entendido
con procesos de 10 mins o mas.

Hasta donde entiendo (que no mucho tampoco), el tema del timeout es
del explorador, el tema del server es que se reinicie el AppPool así
que tendrás que analizar estos casos.

saludos.
nelo

2011/4/18 cibrax <cib...@gmail.com>:

Jorge Fioranelli

unread,
Apr 18, 2011, 6:07:11 PM4/18/11
to altnet-...@googlegroups.com
Tené en cuenta que el timeout de asp.net por defecto son 90 segundos, es decir que cualquier thread que tarde mas de eso va a ser dado de baja. Claro que esto es configurable y puede extenderse a mas tiempo (también hay que extender el de IIS).

asp.net también limita a X threads por CPU (también configurable). Digamos que lo tengo configurado en 25 y tengo 2 CPU, eso da un total de 50 threads disponibles para atender requests simultáneos.

Si subo el timeout a 10 minutos y mando a ejecutar 8 procesos largos, voy a empezar a usar threads del pool, disminuyendo a 42 la cantidad de threads disponibles para atender nuevos requests. Si tengo mas de 42 pedidos simultáneos, se van a empezar a encolar.

Esta solución es válida pero tenés que hacer un poco de cuentas y monitoreo de los perf counters de asp.net para asegurarte que no hay encolamiento (o que por lo menos es bajo).

Respecto al uso de async pages, solo son útiles si el proceso es intensivo de I/O (ej un query muy pesado a la DB o una llamada a un webservice que tarda mucho), pero no ayuda mucho en el caso de que el proceso sea intensivo de CPU (hay que ver cual es tu caso). Tal como dice Pablo, el fire and forget no sirve si no liberás el thread (ej haciendo una llamada async a I/O).

Si podés segmentar el proceso largo en muchos procesos pequeños independientes (muchas veces no se puede por la propia naturaleza del proceso), una alternativa es manejar el proceso largo desde el browser, haciendo llamadas jquery por cada proceso pequeño. Tal como plantea Carlos, pero desde el browser. Esto es similar al enfoque que plantea Nelo pero ejecutando el proceso de a partes ( y le podés robar el código de la progressbar a Nelo :) )

Espero no haberte mareado mucho, si no queda claro preguntá, que para eso estamos!

Saludos

JorgeF
http://blog.jorgef.net
@jorgefioranelli

Carlos Peix

unread,
Apr 18, 2011, 6:21:10 PM4/18/11
to altnet-hispano
Es buena la aclaracion de Pablo y de Jorge.

Estamos hablando de una solucion "barata" y sencilla a un problema muy comun. Incluso podria usarse el Scheduler de Castle Project por ejemplo (no quise mencionarlo antes para no complicar).

La solucion que mencione vale para procesos livianos, digamos, un par de minutos por dia. Si encontras que tenes varios procesos largos, entonces justifica que contrates (o pidas a tu cliente) un hosting dedicado y hagas un servicio Windows o un WebRole de Azure.

En arquitectura, todo solucion es buena en un determinado entorno y mala en muchos otros.

----------------------------------
Carlos Peix

2011/4/18 Jorge Fioranelli <jorge.fi...@gmail.com>

nelopa...@gmail.com

unread,
Apr 18, 2011, 7:56:43 PM4/18/11
to altnet-...@googlegroups.com
Jorge, ¿estás seguro que los 90 segundos son del IIS?... creo que son
del browser, que si tarda mas de eso la respuesta da timeout y no
sigue esperando... en los procesos que yo he hecho no cambiamos nada
del IIS y los procesos funcionan con mas de 90 segundos.

nelo

2011/4/18 Carlos Peix <carlo...@gmail.com>:

Jorge Fioranelli

unread,
Apr 18, 2011, 8:11:40 PM4/18/11
to altnet-...@googlegroups.com
Hola Nelo, 

Que raro.

Desde mi experiencia, los 90 segundos son del runtime de asp.net, son independientes del IIS y del browser. Siempre hay que asegurarse que IIS soporte mas tiempo de timeout, porque sino no importa lo que configures en asp.net, IIS va a cortar primero.

Se configuran en el attributo de "executionTimeout" del tag "httpRuntime", y segun la documentacion de MSDN a partir de .Net 2.0 el dafault subio a 110 segundos: http://msdn.microsoft.com/en-us/library/e1f13641.aspx

Si pones un bucle infinito en una pagina, y la ejecutas, deberias ver un error pasados los 110 segundos.  Todavia no lo probe con asp.net v4, pero recuerdo haber hecho varias pruebas con asp.net v1.1 y v2.0, y ese era el comportamiento.

Un comentario importante es que este timeout solo aplica si el atributo de Debug del web.config tiene valor False.  Esta pensado asi para que no corte el thread cuando uno esta debuggeando la aplicacion.


Saludos

JorgeF
@jorge.fioranelli


Leandro de los Santos

unread,
Apr 18, 2011, 9:01:12 PM4/18/11
to altnet-...@googlegroups.com

Ahora me quedan algunas dudas, así que a repasar código un poco viejito y contar que hice en su momento....

 

1) Aplicación Web que publica un webservice mediante spring (Spring.Web.Services.WebServiceExporter normalito, nada de WCF).

2) Aplicación cliente que invoca al webservice también mediante spring (era un winform, pero para el caso igualmente es un request http al servidor).

3) Una clase que era el proceso publicado en 1, que implementaba la interface

3.1) Generaba un thread y lo apuntaba a un método estático que realmente ejecutaba el proceso.

3.2) Ejecutaba el proceso ("Start" del thread, comienza a correr mi método estatico que ejecuta el proceso)

3.3) Invocaba una clase estatica para pasar el "Id" de la corrida y mantener ciertas estructuras que permitieran revisar el estado del proceso

3.4) Hacia "Start" del thread

3.5) Devolvía  el Id

4) La aplicación cliente invocaba otro método del mismo webservice que devolvía el estado la corrida y actualizaba el gauge... En algún momento el retorno era "termine" y el cliente dejaba de refrescar.

 

Ahora, mirando el código, que ejecutaba sin problemas en hostings de los baratitos...

 

/---- Clase del Servidor (publicada como webservice mediante Spring) ---/

 

namespace SueldosProcess

{

    public class ProcesoCierreMes : IProcessService<ParametroProcesoCierreMes>

       {

        #region IProcessService<ParametroProcesoCierreMes> Members

        public Guid StartProcess(ParametroProcesoCierreMes parametro)

        {

            Guid ProcessId = Guid.NewGuid();

            ParameterizedThreadStart threadParam = new ParameterizedThreadStart(Procesar);

            Thread workerThread = StaticProcessInfo.SetProcessStart(ProcessId, threadParam);

            workerThread.Start(new Object[] { ProcessId });

            return ProcessId;

        }

        public DAOCommon.ProcesoStatusInfo GetStatusInfo(Guid ProcessId)

        {

            return StaticProcessInfo.getInfo(ProcessId);

        }

        #endregion

 

        #region metodo estatico para procesamiento

        private static void Procesar(object state)

        {

            Guid ProcessID = (Guid)(state as Object[])[0];

            StaticProcessInfo.SetProcessCount(ProcessID, 1);

            new AvanzarParametroActivo().Procesar(); //Esta es la clase del proceso

            StaticProcessInfo.finishProcess(ProcessID);

        }

        #endregion

    }

}

 

/---- Clase para manejar threads  ---/

    public static class StaticProcessInfo

    {

        #region privados

        private static Object semaforo = new object();

        private static Dictionary<Guid, ProcesoStatusInfo> dictStatusProceso = new Dictionary<Guid, ProcesoStatusInfo>();

        //TODO: Despues ver como limpias estas instancias...

        private static Dictionary<Guid, Thread> activeThreads = new Dictionary<Guid, Thread>();

        #endregion

 

        public static Thread SetProcessStart(Guid processId, ParameterizedThreadStart threadParam)

        {

            Thread retorno;

            lock (semaforo)

            {

                retorno = new Thread(threadParam);

                activeThreads.Add(processId, retorno);

                ProcesoStatusInfo info = new ProcesoStatusInfo();

                info.CargandoDatos = true;

                info.ProcesoTerminado = false;

                dictStatusProceso.Add(processId, info);

            }

            return retorno;

        }

…. Manejo de diccionarios, en fin… nada del otro lado + algún resource que me acabo de dar cuenta que no liberaba y me hago el gil para que no se note…

 

Bueno, con esto básicamente lograba lanzar un proceso en un thread, dar respuesta al cliente, y luego el thread se ejecutaba en el servidor sin problemas.

 

Este código tenía dos grandes problemas.

 

a)      Que se reciclara el working process en el servidor (igualmente si esto pasa caia mi thread y todos los usuarios conectados…)

b)      Que tuviera más de un working process (cosa que podía controlar tranquilamente con la base de datos).

 

Ahora, una vez que lanzaba el thread, y dejando de lado los dos temas anteriores, ese thread no tenía ningún problema en ejecutarse en el IIS, el proceso corría sin problemas y no frenaba.

 

Saludos

PD: Tengo algo de código de Nelo y de un par más de lectores de este grupo :D

 

 

 

 

 

-----Original Message-----
From: altnet-...@googlegroups.com [mailto:altnet-...@googlegroups.com] On Behalf Of nelopa...@gmail.com
Sent: Monday, April 18, 2011 8:57 PM
To: altnet-...@googlegroups.com

>> > net-mvc-con.html este proceso funciona hoy en día en producción y

Cristian Prieto

unread,
Apr 18, 2011, 9:08:51 PM4/18/11
to altnet-...@googlegroups.com
Porqué en vez de manejar una thread para cada proceso no utilizas una cola y un worker process para manejar los procesos y tareas encoladas?

Implementarlo es hasta facil compañero, imagínate que tienes una cola de mensajes (usa cualquier sistema de colas, hasta el broker de sql server te sirve o puedes implementar hasta tu propia pseudocola con una base de datos como mysql, es de experimentar y jugar, ademas este correo lo escribo mientras espero un deploy, asi que no quiero emocionarme y hacer un super correo de paginas!).

Luego tienes un servicio o proceso que recupere los trabajos puestos en cola y los procesa, ahí si quieres tienes una cantidad n de threads que puedan manejarlo.

Porqué veo un problema con tu implementación? simple, imagínate que tengas mucho tráfico, vas a terminar acabandote las threads disponibles en tu maquina y crear un cuello de botella esforzando la maquinaria de threads del sistema operativo (no es que lo haga mal, pero una app que ya maneja más de 300 threads al mismo tiempo se ve mal!) [ej. outlook].

De hecho lo que te digo no es nada nuevo, asi es como funcionan los buses y aggregators :)

Listo, my deploy terminado... hora de decir hasta luego!

Cheers!


Cristian Prieto


2011/4/19 Leandro de los Santos <delossant...@gmail.com>

Leandro de los Santos

unread,
Apr 18, 2011, 9:45:47 PM4/18/11
to altnet-...@googlegroups.com

Cristian,

tiene lógica hacerlo como vos decís, pero para el batch que tengo por resolver mantener un thread vivo y encolarle un proceso es tener vivo un thread 23.45 hs esperando un mensaje (es un batch nocturno que cambiara a lo sumo 50 registros x día y sus respectivos mails).

 

Igualmente coincido con la idea de “si moves volumen, pedile una VPS o azure” en lugar de un shared hosting, en un entorno así hasta pensaría en un servicio con lógica asíncrona para manejar algunas cuestiones como enviar emails, etc. Pero para esta aplicación no quiero gastar más tiempo en hacer “arquitectura” que en resolver el proceso.

 

Saludos

cibrax

unread,
Apr 19, 2011, 10:21:22 AM4/19/11
to AltNet-Hispano
Gracias a este thread descubri que si existen async controllers en MVC
(http://msdn.microsoft.com/en-us/library/ee728598.aspx)
Igual como menciono Jorge, ASP.NET va a matar el thread y cualquier
cosa que este haciendo si se excede del timeout (http://
stackoverflow.com/questions/4898091/time-out-problem-with-thread-in-
asp-net)

Saludos
Pablo.

On Apr 18, 10:45 pm, "Leandro de los Santos"
> 2011/4/19 Leandro de los Santos <delossantos.lean...@gmail.com>
> [mailto:altnet-...@googlegroups.com] On Behalf Of nelopause...@gmail.com
> Sent: Monday, April 18, 2011 8:57 PM
> To: altnet-...@googlegroups.com
>
> Subject: Re: [altnet-hispano] Re: ASP.NET MVC | Procesos "intensivos en el
> servidor"
>
> Jorge, ¿estás seguro que los 90 segundos son del IIS?... creo que son del
> browser, que si tarda mas de eso la respuesta da timeout y no sigue
> esperando... en los procesos que yo he hecho no cambiamos nada del IIS y los
> procesos funcionan con mas de 90 segundos.
>
> nelo
>
> 2011/4/18 Carlos Peix < <mailto:carlos.p...@gmail.com>
> carlos.p...@gmail.com>:
>
> > Es buena la aclaracion de Pablo y de Jorge.
>
> > Estamos hablando de una solucion "barata" y sencilla a un problema muy
>
> > comun. Incluso podria usarse el Scheduler de Castle Project por
>
> > ejemplo (no quise mencionarlo antes para no complicar).
>
> > La solucion que mencione vale para procesos livianos, digamos, un par
>
> > de minutos por dia. Si encontras que tenes varios procesos largos,
>
> > entonces justifica que contrates (o pidas a tu cliente) un hosting
>
> > dedicado y hagas un servicio Windows o un WebRole de Azure.
>
> > En arquitectura, todo solucion es buena en un determinado entorno y
>
> > mala en muchos otros.
>
> > ----------------------------------
>
> > Carlos Peix
>
> > 2011/4/18 Jorge Fioranelli < <mailto:jorge.fiorane...@gmail.com>
> jorge.fiorane...@gmail.com>
> ...
>
> read more »- Hide quoted text -

Angel Java Lopez

unread,
Apr 19, 2011, 11:07:13 AM4/19/11
to altnet-...@googlegroups.com
Hola gente!

Hmmm.. no estoy convencido de:

"A background thread can only live as long as the thread that calls it is alive."
que se menciona en
stackoverflow.com/questions/4898091/time-out-problem-with-thread-in-
asp-net
)
No es asi como en general se manejan los "background" threads:
http://msdn.microsoft.com/en-us/library/h339syd0%28v=vs.71%29.aspx
pero tal vez malentendi al autor de esa respuesta.

No deberia haber, entonces, limite en el tiempo de un thread creado con new Thread y .Start. (Igual entiendo que en este hilo, se discutio del Thread que atiende la pagina, no de un new Thread; pero esa frase de arriba parece referirse al tema de los (new Thread()).Start())

Otro tema: pondria newthread.IsBackground = true, para que muera cuando ya no haya threads de los "normales" (escribo de memoria, es IsBackground la propiedad?)

Nos leemos!

Angel "Java" Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez


2011/4/19 cibrax <cib...@gmail.com>

Leonardo Micheloni

unread,
Apr 19, 2011, 7:33:04 PM4/19/11
to altnet-...@googlegroups.com
tuve que modificar algunos procesos que empezaron a consumir muchos recursos por temas de volumen, pasé por la aplicación web sincrónica, los bgtheads y los async, al final la aplicación marca en la base una suerte de estado / bandera + datos para que un servicio de windows se levante y haga el trabajo pesado, de esta manera también para dar la sensación de que lo controla la web la pantalla que antes hacía el trabajo consulta esta tabla que se va a actualizando mientras el servicio avanza, también se puede cancelar de esa manera, es medio rústico pero al final lanzando thread desantendidos en IIS (que siguen vivos por más que el que los generó muera) me quitaba control, y muchas veces el mismo IIS lo barría si hacía cosas raras.....

2011/4/19 Angel Java Lopez <ajlop...@gmail.com>

Ariel Piñeiro

unread,
Apr 19, 2011, 7:37:56 PM4/19/11
to altnet-...@googlegroups.com
Leandro,
              recién veo tu inquietud. Procesos + Batch + Thread + Triggers = Cron + Quartz, quizás alguien ya lo mencionó.


Yo tuve que hacer algo similiar y utilizaba un sitio MVC para administrarlo, utilizando en el backoffice .NET Remoting. La idea es que en el servidor se registran clases que ejecutan procesos (o lo que quieras). En fin, investigalo.


Saludos,
Ariel Piñeiro
http://ar.linkedin.com/in/arielpineiro



2011/4/19 Leonardo Micheloni <leonardogabr...@gmail.com>

Cristian Prieto

unread,
Apr 19, 2011, 7:54:39 PM4/19/11
to altnet-...@googlegroups.com
Hay una técnica legendaria en ASP.NET para cuando uno suele hacer tareas de background como las típicas de enviar correos y cosas como esas, y es usando Thread timers y background threads.

La idea es simple, al inicio de la aplicación (i.e.: en el App_Start de tu Global.asax) creas un timer que chequee cada cierto tiempo tu cola de cosas por hacer, simple, sencillo :) 

Algo interesante que puede suceder es que tu thread por alguna u otra razon muera cuando la app pool muera, puede pasar! quien sabe :) bueno, porqué no manejar entonces lo que suceda de forma en que consideres una sola transacción por pasada? (no se como es tu proceso, pero puedes jugar un poco con la forma en q lo hagas, y porqué no, hasta jugar con compensaciones si no tienes soporte para una transacción).

Bueno, cuentame si estoy alejado de lo que preguntas o simplemente escribo un short code para ejemplificar mi punto...

Hora de retornar a mi compile time...

Cristian Prieto

PD: Angel, si, es IsBackground, threads como las del timer son por defecto background threads.

2011/4/20 Angel Java Lopez <ajlop...@gmail.com>

Leandro de los Santos

unread,
Apr 21, 2011, 2:08:47 PM4/21/11
to altnet-...@googlegroups.com

Me gusto esta idea, me convenció el “legendaria”…

 

Quedo esto (aprovechando framework 4)

 

/----- Global Asax -----/

 

        protected void Application_Start()

        {

 

            Task.Factory.StartNew(() => {

                //Bucle hasta que el end marque que termino

                while (!ProcessQueue.Queue.IsCompleted)

                {

                    try

                    {

                        var proceso = ProcessQueue.Queue.Take();

                        proceso.Procesar();

                    }

                    catch (InvalidOperationException) { } //Cuando marco que termine de agregar, el take raisea esta excepcion y sale...

                    catch (Exception e) { } //La vida continua :D

                }

            }, TaskCreationOptions.LongRunning);

        }

 

        protected void Application_End()

        {

            ProcessQueue.Queue.CompleteAdding(); //Marco la cola como completa para que cierre el tread.

        }

 

 

/---- “ProcessQueue” ---/

    public static class ProcessQueue

    {

        static ProcessQueue()

        {

            Queue = new BlockingCollection<IProcessCommand>();

        }

        public static BlockingCollection<IProcessCommand> Queue {get; private set;}

    }

 

 

/---- La interfaz para procesos -----/

    public interface IProcessCommand

    {

        void Procesar();

    }

 

 

 

/---- El código en el controller -----/

        public ActionResult Index()

        {

            ProcessQueue.Queue.Add(new PeridicBatchCommand());//PeriodicBatchCommand implementa IProcessCommand

            return View();

        }

 

 

En resumen

è Creo un task usando TaskFactory de .net 4 (le doy el hint de long running)

è Uso un BlockingList para sincronizar

o   Cuando el task hace ProcessQueue.Queue.Take()

§  Si hay algo, lo saca y lo procesa

§  Si no hay nada, duerme al thread esperando a que agregue algo (no tengo que usar el timer)

§  Si marco a la lista como completada, salta una excepción “InvalidOperation”, el bucle infinito se muere

è Desde los controllers simplemente agrego el proceso a la cola

è En el application_end marco la cola como completada para que se cierre el bucle infinito.

 

Obviamente se puede escalar mucho esta solución, pero antes de escalar algo así me parece que primero intentaría aprovechar el consejo que me dieron de usar quartz (ya maneja colas en la base, etc., parece muy bueno).

 

Saludos y gracias a todos

Juanma

unread,
Apr 26, 2011, 12:21:13 PM4/26/11
to altnet-...@googlegroups.com
No sé cómo de fiable es tener el código en el Application_Start.

Si se cae el IIS, o simplemente decide reciclar el AppPool, no se
reactivará la aplicación hasta que llegue la siguiente petición, por
lo que dejarías de ejecutar las tareas que tengas en background.

En este artículo te lo explica bastante bien, aunque es de hace tiempo
y puede que hayan cambiado las cosas:
http://msdn.microsoft.com/en-us/magazine/cc163821.aspx

Saludos,

Juanma.

2011/4/21 Leandro de los Santos <delossant...@gmail.com>:

Carlos Admirador

unread,
Apr 7, 2015, 4:50:16 PM4/7/15
to altnet-...@googlegroups.com
No sé cómo acabaría la cosa, atención al Background en ASP.NET y sus problemas como ya comentan Haacked o Ayende

Phil Haack wrote a great article on the dangers of recurring background tasks in ASP.NET
http://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx/

http://ayende.com/blog/155489/rotten-scheduling-dont-roll-your-own#comments


Y si se sigue adelante considerar todo estos frameworks y tools antes de reinventar la rueda

Un montón de alternativas como HangFire, Quarz.net y muchas más
http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx


http://blog.koalite.com/2012/05/programacion-de-tareas-con-quarz-net/

Carlos Admirador

unread,
Mar 14, 2018, 3:40:02 PM3/14/18
to AltNet-Hispano
Leandro, al final cómo evolucionó esta idea? Funcionó bien producción... ? código real world colosal!!

Kiquenet

unread,
Apr 18, 2024, 12:37:29 PM4/18/24
to AltNet-Hispano
estado del arte actual ? 
vigente aún  Procesos + Batch + Thread + Triggers = Cron + Quartz ?

Kiquenet

unread,
Dec 26, 2025, 7:00:15 PM12/26/25
to AltNet-Hispano
Hangfire en NET 4.8 y NET 6/7/8

https://www.jimmybogard.com/tales-from-the-net-migration-trenches-hangfire/
Hangfire is an easy way to perform background tasks/processes in a .NET web application, and it also supports persistent storage for both the jobs and queues. I use it quite a lot in applications where I don't want to introduce a separate host for processing messages, or introduce a specific queue/broker for background jobs. Hangfire supports fire-and-forget jobs as well as "cron"-based jobs. It also provides a nice dashboard where you can see completed and failed jobs, with the option of retrying failed jobs as desired.

Depending on how you're using Hangfire, it introduces a unique challenge when migrating from .NET 4.8 to .NET 6/7/8. Hangfire supports both frameworks, but as usual, the devil is in the details. We want to be able to start/consume jobs from both sides AND ensure our job executes at most once.

 It's weakness in my experience was the following two areas:
1) Compared to Azure Functions... Azure Functions just scales better for speed and performance. But you pay the price in owning the environment. Hangfire dashboard is fantastic. I wish MS would build something remotely as good as the Hangfire Dashboard.
2) Multi-Tenancy is really difficult in hangfire. Haven't tried it yet in Core... But it was unreliable in ASP.Net 4.8 with random failures.

If these scenarios aren't in your use case... Hangfire is the BOMB. Love it!

Reply all
Reply to author
Forward
0 new messages