Quartz .NET
Quartz .NET
Introduzione
Quartz è una libreria open source (nata in ambiente Java)
che permette di schedulare l’esecuzione del codice scritto all’interno di
classi, chiamate job, in modo
semplice ma allo stesso tempo con un livello di personalizzazione molto elevato. I job sono classi .NET standard che
implementano l’interfaccia IJob della libreria. La schedulazione dei job
avviene tramite i trigger che offrono funzioni per la configurazione delle
condizioni sotto cui i job stessi devono essere eseguiti.
Caratteristiche
Come detto, la schedulazione (tramite i trigger) aiuta
nella definizione del momento in cui un certo job deve essere eseguito e con
quale ricorrenza. I quartz scheduler
si occupano infine di associare a ciascun trigger un job. L’esecuzione dei job
avviene all’interno di thread (quartz utilizza un ThreadPool) inizializzati e gestiti dalla libreria;
pertanto lo sviluppatore non deve preoccuparsi delle difficoltà insite
nell’utilizzo dei thread, né della scrittura del codice per la schedulazione.
Configurazione
Quartz.NET è una libreria
molto configurabile e vengono fornite tre vie per la configurazione:
·
Programmaticamente
usando una NameValueCollection
·
Utilizzando
un file chiave/valore di nome quartz.config
·
Utilizzando
il classico app.config con la sezione <quartz-element>
Al momento della
scrittura dell’articolo, sulla documentazione on-line (http://www.quartzscheduler.net/documentation) non è ancora presente la descrizione di tutte le
proprietà che è possibile configurare. Per fortuna è possibile fare riferimento
alla documentazione della contro parte java della libreria (http://www.quartz-scheduler.org/documentation/quartz-2.x/configuration/ConfigMain).
Esempi di proprietà configurabili
sono le dimensioni del thread pool, il nome da assegnare ai thread, le modalità
di memorizzazione delle informazioni di scheduling, ecc…
Esempio di configurazione
tramite quartz.config:
quartz.scheduler.instanceName = MyScheduler
quartz.threadPool.threadCount = 3
La
stessa configurazione la possiamo ottenere programmaticamente in questo modo:
NameValueCollection properties = new NameValueCollection();
properties["quartz.scheduler.instanceName"] = "MyScheduler";
properties["quartz.threadPool.threadCount"] = 3;
NameValueCollection properties = new NameValueCollection();
properties["quartz.scheduler.instanceName"] = "MyScheduler";
properties["quartz.threadPool.threadCount"] = "3";
var factory = new StdSchedulerFactory(properties);
IScheduler scheduler
= factory.GetScheduler();
Un semplice
esempio
Iniziamo
con un esempio che ci permetterà di analizzare i principali elementi della
libreria. Innanzitutto creiamo un job, una classe, come detto, che implementi
l’interfaccia IJob e definiamo il metodo Execute.
/// <summary>
/// Un semplice
Job (implementa IJob)
/// </summary>
public class FirstJob : IJob
{
/// <summary>
/// Implementazione del metodo che
verrà invocato quando le condizioni
/// definite nel trigger saranno
soddisfatte
/// </summary>
/// <param
name="context"></param>
public void Execute(IJobExecutionContext context)
{
Console.WriteLine("{0} job in execution", GetType().FullName);
//
codice custom
}
}
All’interno
del metodo Execute è possibile eseguire il nostro
codice. E’ questo il metodo che chiamerà il trigger al momento opportuno. Vediamo
ora come si configura un trigger e gli si associa il job appena definito.
class Program
{
static void Main(string[] args)
{
try
{
// Recupero una istanza dello scheduler dalla Factory
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
// lo scheduler viene avviato
scheduler.Start();
// costruisco un’istanza di IJobDetail e la lego al mio FirstJob
IJobDetail job = JobBuilder.Create<FirstJob>().WithIdentity("firstjob", "test").Build();
// Dico al trigger di far
partire subito il job e di rieseguirlo ogni 10 secondi
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", " test")
.StartNow()
.WithSimpleSchedule(x
=> x
.WithIntervalInSeconds(10).RepeatForever()).Build();
// Associo il job al trigger
scheduler.ScheduleJob(job,
trigger);
// questa sleep
mi permette di vedere qualcosa sulla console
Thread.Sleep(TimeSpan.FromSeconds(60));
// alla fine fermo lo scheduler prima di uscire dall’applicazione
scheduler.Shutdown();
}
catch (SchedulerException se)
{
Console.WriteLine(se);
}
Console.WriteLine("Press any key to
close the application");
Console.ReadKey();
}
}
Innanzitutto è necessario
repuperare un’istanza dello scheduler tramite la factory e su questa chiamare
il metodo Start; a seguito di questa
chiamata quartz inizierà a gestire (attraverso i cosidetti daemon threads) i
trigger ed i job che verranno configurati fino a quando non verrà chiamato il
metodo Shutdown.
Utilizzando il metodo statico Create della classe JobBuilder è facile
creare un’istanza di IJobDetail associando ad essa il job che abbiamo
precedentemente definito. Al job dobbiamo associare un nome e facoltativamente
anche un gruppo di appartenenza. Il gruppo permette allo sviluppatore di dare
delle etichette sotto le quali organizzare in modo più chiaro ed ordinato i
vari job.
JobDataMap
Durante la creazione dei
nostri job possiamo fornirgli dei parametri che saranno disponibili durante
l’esecuzione dei job stessi all’interno del metodo Execute. A tal fine la libreria
espone la JobDataMap, un’implementazione di IDictionary, i cui elementi devono essere serializzabili.
// definisco il job precedente passando dei parametri nella mappa
IJobDetailjob=JobBuilder.Create<FirstJob>()
.WithIdentity("firstjob","test")
.UsingJobData("stringa","Hello World!")
.UsingJobData("valorefloat",3.141f)
.Build();
Il codice precedente
mostra quanto sia semplice aggiungere elementi alla JobDataMap.
public void Execute(IJobExecutionContext context)
{
Console.WriteLine("{0} job in execution", GetType().FullName);
JobDataMapdataMap=context.MergedJobDataMap;
stringjobSays=dataMap.GetString("jobSays");
floatmyFloatValue=dataMap.GetFloat("myFloatValue");
// codice custom del nostro job
}
L’esempio dimostra come recuperare i valori dalla mappa,
definita in fase di creazione del job. La mappa si trova all’interno
dell’istanza di IJobExecutionContext che ci viene fornita come parametro in ingresso del metodo
Execute.
Conclusioni
La libreria
si presta ad essere utilizzata sia in scenari enterprise
(è tranquillamente in grado di gestire centinaia di job) che (data la
semplicità di configurazione ed utilizzo) per piccole applicazioni in cui sia
necessario l’esecuzione di routine ad intervalli regolari. Infine, la
documentazione on-line e gli esempi forniscono tutto il materiale necessario alla
schedulazione secondo le esigenze di ciascuna applicazione.
Commenti
Posta un commento