Ejecutando un servicio en BackGround en IIS (Azure)


Objetivo

El objetivo es lanzar la un tarea en background desde el IIS de manera simple; evitando los costes y/o complejidades innecesiarias (Azure Worker Role, Windows Service…).

Propuesta

Hay un excelente articulo The Dangers of Implementing Recurring Background Tasks In ASP.NET de Phil Haack donde se describen los problemas asociados a las tareas en background lanzadas desde IIS.

El problema consiste básicamente en el parada de los proceso Web del IIS:

  • Por modificación del Web.Config
  • Reciclado del pool del IIS ya sea tras 29 horas o por inactividad del mismo
  • Reinstalación de la aplicación

La propuesta de Phil Haack; se basa en registrar el job a los eventos del proceso de Web mediante HostingEnvironment.RegisterObject. Cuando nos registramos ante la parada somos notificados mediante el metodo Stop de la interfaz IRegisteredObject.

Existen dos problemas:

  • El articulo propone que antes de la tarea periodica; se verifique que no se ha solicitado la parada del job para proceder en consecuencia. El problema existe cuando la tarea peridiodica tarda más de los 30 segundos que nos ofrece de timeout del método Stop. En este caso no funcionaria.
  • Otro problema es el parada inesperada de la aplicación, ya sea por cuelgue, ejecución del comando IISReset, muerte del proceso IIS… En estos casos la aplicación no tiene notificación de este evento con lo que tampoco funcionaria.

Propuesta Modificada

Para solventarlo;  la solución propuesta consiste en definir una acción de parada independientemente que la tarea periodica este en curso. Esto puede provocar problemas en la tarea periodica que deberíamos controlar; que igualmente ocurrirían. Pero podemos asegurarnos en la acción de parada de tomar las medidas oportunas para no dejar recursos bloqueados.

Además en el reinicio del JobHost se fuerza una parada del mismo; por si la aplicación se paro de forma inesperada (En este caso los recursos solo se liberarían al arrancar de nuevo la aplicación).

En el constructor de la clase JobHost; pasamos dos delegados: uno para abrir los recursos y otro para cerrarlos. El delegado de cierre se llamara automáticamente al parar el Pool Web del IIS y al reiniciar la aplicación.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Web.Hosting;

namespace Infrastructure.Job
{
    public class JobHost : IRegisteredObject
    {
        private bool _shuttingDown;
        private readonly Action _jobShuttdownAction;

        public JobHost(Action jobStartAction, Action jobShuttdownAction)
        {
            HostingEnvironment.RegisterObject(this);
            if (jobShuttdownAction != null) jobShuttdownAction();
            if (jobStartAction!=null) jobStartAction();
            _jobShuttdownAction = jobShuttdownAction;
        }

        public void Stop(bool immediate)
        {
            if (_shuttingDown) return;
            _shuttingDown = true;
            if (_jobShuttdownAction != null) _jobShuttdownAction();
            HostingEnvironment.UnregisterObject(this);
        }

        public void Run(Action jobWorkAction, int sleepMilliseconds)
        {

            while (true)
            {
                if (_shuttingDown)
                {
                    return;
                }
                try
                {
                  jobWorkAction();
                } catch {}
                Thread.Sleep(sleepMilliseconds);
            }
        }
    }
}

En Application_Start lanzamos nuestro Job con la clase JobHost

ThreadPool.QueueUserWorkItem(state => new JobHost(MyJob.Start,MyJob.Stop).Run(MyJob.DoWork, 100));
About these ads
Esta entrada fue publicada en Azure, Desarrollo y etiquetada , , . Guarda el enlace permanente.

Deja un comentario

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s