Categorías: Sin categoría

Hangfire: Como crear procesos en segundo plano con C#

En nuestros desarrollos, a menudo tenemos la necesidad de ejecutar tareas en segundo plano. Pensemos en una aplicación web, por ejemplo. Imaginemos que tenemos que actualizar información de nuestra base de datos cada cierto tiempo y de forma desatendida o quizás ejecutar un costoso proceso de descarga de ficheros. Para realizar estas tareas, podríamos desarrollar servicios Windows e instalarlos en el servidor, incluso utilizar WebJobs de Azure si estamos en un entrono cloud.

También tenemos otra vía: Utilizar un framework para la programación y ejecución de tareas en segundo plano. Hay muchos frameworks para esto, incluso .NET tiene la API de QueueBackgroundWorkItem que intenta abordar este problema, pero en este post nos vamos a centrar en Hangfire.

¿Qué es Hangfire?

Hangfire es uno de los frameworks mas completos para la creación de tareas en segundo plano en aplicaciones .NET e incluso .NET Core. Tiene muchísimas características chulas (algunas de ellas en su versión PRO) y además una documentación genial.

Para poder entender de verdad como funciona, vamos a ver su arquitectura. Simplificando, Hangfire consta de tres partes:

1. Cliente

En nuestro hilo principal de ejecución , simplemente tenemos que instanciar el cliente o utilizar métodos estáticos que nos proporciona la clase BackgroundJob.

En el momento en el que Hangfire serialice la definición de la tarea en el sistema de almacenamiento que hayamos seleccionado, volveremos a tener el control de nuestro hilo principal y la tarea se encolará para su proceso.

// Podemos lanzar tareas "fire-and-forget"
BackgroundJob.Enqueue(() => Console.WriteLine("¡Hola desde background!"));

// Crear tareas y posponerlas
BackgroundJob.Schedule(() => Console.WriteLine("¡Hola desde hace media hora!"), TimeSpan.FromMinutes(30));

// Incluso programar tareas recurrentes
RecurringJob.AddOrUpdate(() => Console.Write("¡Soy una tarea diaria!"), Cron.Daily);

 2. Almacenamiento

Hangfire mantiene nuestras tareas guardadas en un sistema de almacenamiento. Esto permite llevar un historial de las estadísticas de los trabajos e incluso reanudarlos en el caso de que nuestra aplicación se caiga por completo.

Podemos mantener nuestras tareas en SQL Server, MongoDB, Raven e incluso en Redis si estamos dispuestos a comprar licencia PRO. Podemos encontrar todos los sistemas de almacenamiento soportados en la documentación.

El histórico de los trabajos se gestiona automáticamente, así que no es necesario ir vaciando el sistema de almacenamiento, Hangfire lo hace por nosotros.

3. Servidor

Hangfire Server es la parte encargada de la ejecución de las tareas. Este servidor se puede iniciar desde cualquier tipo de aplicación, ya sea una aplicación de consola o un servicio web. Simplemente instanciamos un objeto BackgroundJobServer y lo desechamos cuando hayamos terminado con él.

var server = new BackgroundJobServer();

// Es importante hacer dispose del servidor
server.Dispose();

Este servidor posee una cantidad configurable de hilos de proceso en segundo plano en los que encolará las tareas guardadas en el almacenamiento y actualizará su estado conforme vaya ejecutando.

Además, en el caso de que se produzca algún tipo de excepción no controlada en la tarea, por defecto se volverá a encolar hasta un máximo de diez veces en un periodo de alrededor de dos horas. Si se supera este límite, la tarea se tendrá que reanudar a mano desde el panel o mediante código. Todo esto es fácilmente personalizable desde código. Solo hay que añadir el siguiente atributo sobre el método que ejecutamos en segundo plano:

// Solo se reintentará una vez
[AutomaticRetry(Attempts = 1)]
public void BackgroundMethod()
{
}

Escalado

En el caso de que necesitemos más de un servidor de Hangfire para nuestra aplicación, solo tenemos que asegurarnos de que todos los servidores tengan el código necesario para llevar a cabo las tareas y conectar dichos servidores al mismo almacenamiento. Detecta cuando un servidor se conecta al almacenamiento, lo añade a su pool de servidores y en el momento que se desconecte, lo elimina y sigue funcionando correctamente. Así de fácil.

Hangfire se encarga de repartir los trabajos conforme sea necesario, en principio de forma equitativa, pero podemos configurar qué trabajos van a qué servidores utilizando colas. Esto nos permite tener servidores dedicados a colas de alta prioridad y compartir servidor para trabajos de menor prioridad.

Instalación

En una aplicación web con SQL Server, es tan fácil como instalar los paquetes necesarios. Para ello, abrimos la consola de administración de paquetes NuGET y escribimos lo siguiente:

PM> Install-Package Hangfire

Esto nos instala Hangfire.Core, el paquete para almacenamiento mediante SQL Server y Microsoft.Owin.Host.SystemWeb.

Para configurarlo, simplemente tenemos que añadir el espacio de nombres de Hangfire a la clase punto de entrada de nuestra aplicación y utilizar los métodos de extensión de la propiedad GlobalConfiguration.Configuration. Por ejemplo, para una aplicación basada en OWIN, añadiríamos lo siguiente a la clase Startup.cs:

public void Configuration(IAppBuilder app)
{
    GlobalConfiguration.Configuration.UseSqlServerStorage("<Cadena de conexión>");
    app.UseHangfireDashboard();
}

Con esto ya tendríamos listo nuestro Hangfire funcionando, con las tareas almacenadas en un SQL Server e incluso con el panel de administración disponible al acceder a http://tuaplicación/hangfire.

Hay que tener en cuenta que dicho panel de administración sólo está disponible para las peticiones locales, aunque esto se puede cambiar.

Conclusión

Hangfire es un framework de tareas en segundo plano con el que se puede hacer procesos muy costosos sin que prácticamente haya impacto alguno en la experiencia del usuario.

Además, viene con un panel de administración genial y tiene una de las mejores documentaciones de un proyecto open source que yo haya visto.

Personalmente, me declaro fan incondicional de este «juguetito».

Compartir
Publicado por
Juan Carlos Martínez García

Este sitio web utiliza cookies para que tengas la mejor experiencia de usuario. Si continuas navegando, estás dando tu consentimiento para aceptar las cookies y también nuestra política de cookies (esperemos que no te empaches con tanta cookie 😊)