Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
October 27, 2022 09:48 pm GMT

ASP.NET: CRON Service Worker

Introduccin

En este artculo veremos como crear un servicio en segundo plano que se ejecutar segn un programa de intervalos. Este ser expresado como si fuera una tarea CRON de Linux, que son en esencia, tareas programadas.

Nota : Aqu encuentras el cdigo fuente

El formato cron se expresa de la siguiente manera:

                                       Allowed values    Allowed special characters   Comment second (optional)       0-59              * , - /                        minute                0-59              * , - /                         hour                0-23              * , - /                          day of month      1-31              * , - / L W ?                     month           1-12 or JAN-DEC   * , - /                            day of week   0-6  or SUN-SAT   * , - / # L ?                Both 0 and 7 means SUN     * * * * * *

Donde 5 o 6 caracteres representan el intervalo de tiempo en el que la tarea se ejecutar, ejemplo:

ExpresinDescripcin
* * * * *Cada minuto
0 0 1 * *A media noche, en da primero de cada mes
0 0 * * MON-FRIA las 0:00, de Lunes a Viernes

Nota : Si quieres conocer ms, puedes visitar este repositorio HangfireIO/Cronnos

Implementacin en ASP.NET Core y Hosted Services

Antes de seguir, realizar este tipo de background services en asp.net core es cada vez ms fcil, tan fcil que ya existen soluciones como HangFire y Azure Functions que realizan este tipo de tareas (tambin basadas en formato CRON). Pero si de igual forma, quisieras aprender hacer tu implementacin (a veces es mejor keep it simple) te recomiendo seguir leyendo .

Para comenzar, crearemos un proyecto web vaco o de consola, da igual ya que no utilizaremos ningn endpoint HTTP, pero puedes mezclarlos sin problema.

dotnet new web -o BackgroundJob.Cron

Instalamos la librera Cronos para poder parsear expresiones CRON.

dotnet add package Cronos

CronBackgroundJob

Esta clase base y abstracta, ser la que se encargar de ejecutar un proceso segn un intervalo de tiempo, este intervalo ser definido como ya lo hemos dicho, con una expresin CRON.

using Cronos;namespace BackgroundJob.Cron.Jobs;public abstract class CronBackgroundJob : BackgroundService{  private PeriodicTimer? _timer;  private readonly CronExpression _cronExpression;  private readonly TimeZoneInfo _timeZone;  public CronBackgroundJob(string rawCronExpression, TimeZoneInfo timeZone)  {    _cronExpression = CronExpression.Parse(rawCronExpression);    _timeZone = timeZone;  }  protected override async Task ExecuteAsync(CancellationToken stoppingToken)  {    DateTimeOffset? nextOcurrence = _cronExpression.GetNextOccurrence(DateTimeOffset.UtcNow, _timeZone);    if (nextOcurrence.HasValue)    {           var delay = nextOcurrence.Value - DateTimeOffset.UtcNow;       _timer = new PeriodicTimer(delay);      if (await _timer.WaitForNextTickAsync(stoppingToken))      {              _timer.Dispose();        _timer = null;        await DoWork(stoppingToken);        // Reagendamos        await ExecuteAsync(stoppingToken);      }    }  }  protected abstract Task DoWork(CancellationToken stoppingToken);}
  • PeriodicTimer: Es un nuevo Timer que nos permite "esperar" el siguiente "Tick" del timer. Es decir, si queremos que el timer se ejecute cada 60 segundos, el mtodo WaitForNextTickAsync estar en modo await hasta que hayan transcurrido esos 60 segundos. Este mtodo regresa true si el intervalo se cumpli y nadie cancel la tarea, regresar false si el stoppingToken cancel la ejecucin.
    • Creamos el timer con la diferencia de tiempo (TimeSpan) entre la fecha y hora actual y la fecha y hora de la siguiente ocurrencia, es decir: Si estamos 27/10/2022 15:00 y la siguiente ocurrencia es el 27/10/2022 16:00, hay una diferencia de 1 hora (3600000 milisegundos), hasta que pase ese tiempo, el PeriodicTimer lanzar su Next Tick.
  • CronExpression: Nos ayuda a entender una expresin cron, en este caso tenemos que darle una fecha y un uso horario (este opcional) para que se pueda determinar cundo ser la siguiente ocurrencia (o sea, la siguiente fecha y hora en que se debe de correr la tarea)
    • GetNextOcurrence: Regresa un DateTimeOffset con la fecha a futuro en donde toca ya correr la tarea.
  • WaitForNextTickAsync: Este mtodo genera un Task que se espera hasta que ocurra el siguiente Tick del Timer.
    • Cada ejecucin liberamos el timer para que en el siguiente ciclo volverlo a crear con la siguiente ocurrencia del Cron, ya que esto no necesariamente ser una espera esttica o igual en cada Tick.
    • Un ejemplo es si pongo que corra de lunes a viernes a las 9PM, la ejecucin de jueves a viernes esperar 24 horas, pero de viernes a lunes esperar 72 horas.
  • DoWork: Este mtodo abstracto ser el que se ejecutar en cada ocurrencia, es abstracto porque cada Worker que hagamos, har una tarea diferente.

Al terminar de correr el DoWork de forma recursiva, mandamos a llamar nuevamente la tarea para agendar la siguiente ejecucin, esto durar por siempre o hasta que el stoppingToken diga lo contrario.

CronSettings

Para poder correr el worker/job anterior, debemos de poder tener una expresin cron y aparte el uso horario que se quiera considerar.

namespace BackgroundJob.Cron.Jobs;public class CronSettings<T>{  public string CronExpression { get; set; } = default!;  public TimeZoneInfo TimeZone { get; set; } = default!;}

CronBackgroundJobExtensions

Para hacer fcil esta integracin entre los options y cada background job, es mejor crear este mtodo de extensin que nos ayudar a registrar cada dependencia de cada job.

namespace BackgroundJob.Cron.Jobs;public static class CronBackgroundJobExtensions{    public static IServiceCollection AddCronJob<T>(this IServiceCollection services, Action<CronSettings<T>> options)        where T: CronBackgroundJob    {        if (options == null)        {            throw new ArgumentNullException(nameof(options));        }        var config = new CronSettings<T>();        options.Invoke(config);        if (string.IsNullOrWhiteSpace(config.CronExpression))        {            throw new ArgumentNullException(nameof(CronSettings<T>.CronExpression));        }        services.AddSingleton<CronSettings<T>>(config);        services.AddHostedService<T>();        return services;    }}

Usamos el Options Pattern muy comn en ASP.NET para registrar cada background job que necesitemos.

Es obligatorio que se indique una configuracin por medio de CronSettings<T> y tambin es obligatorio tener una expresin cron.

MySchedulerJob

Este ser el background job de ejemplo:

namespace BackgroundJob.Cron.Jobs;public class MySchedulerJob : CronBackgroundJob{    private readonly ILogger<MySchedulerJob> _log;    public MySchedulerJob(CronSettings<MySchedulerJob> settings, ILogger<MySchedulerJob> log)        :base(settings.CronExpression, settings.TimeZone)    {        _log = log;    }    protected override Task DoWork(CancellationToken stoppingToken)    {        _log.LogInformation("Running... at {0}", DateTime.UtcNow);        return Task.CompletedTask;    }}

Realmente lo nico que hace es escribir en los logs la fecha en la que se ejecut y as poder comprobar que todo funciona.

Program

Para finalizar, registramos las dependencias con la extensin que escribimos y vual, ya podemos correr.

using BackgroundJob.Cron.Jobs;var builder = WebApplication.CreateBuilder(args);builder.Services.AddCronJob<MySchedulerJob>(options => {    // Corre cada minuto    options.CronExpression = "* * * * *";    options.TimeZone = TimeZoneInfo.Local;});var app = builder.Build();app.Run();

Y el resultado:

Image description

Conclusin

A pesar de que ya existen soluciones que nos ayuda implementar este tipo de tareas, mantener las cosas simples a veces es la opcin que necesitas por que la tarea es simple.

Si necesitas algo que escale, que sea resiliente, flexible a un costo de tiempo bajo, definitivamente te recomiendo irte por Azure Functions. Si no ests en Azure, puedes irte por Hangfire.

Pero si lo que necesitas son tareas programadas y no depender de Azure, los Hosted Services es una buena opcin.

Referencias


Original Link: https://dev.to/isaacojeda/aspnet-cron-service-worker-2no9

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To