Introdução ao quartzo

Introdução ao quartzo

1. Visão geral

Quartz é uma estrutura de programação de trabalhos de código aberto escrita inteiramente em Java e projetada para uso em aplicativosJ2SEeJ2EE. It offers great flexibility without sacrificing simplicity.

Você pode criar agendamentos complexos para executar qualquer trabalho. Exemplos são e. tarefas que são executadas diariamente, todas as sextas-feiras às 19:30 ou apenas no último dia de cada mês.

Neste artigo, daremos uma olhada nos elementos para construir um trabalho com a API Quartz. Para uma introdução em combinação com Spring, recomendamosScheduling in Spring with Quartz.

2. Dependências do Maven

Precisamos adicionar a seguinte dependência aopom.xml:


    org.quartz-scheduler
    quartz
    2.3.0

A versão mais recente pode ser encontrada emMaven Central repository.

3. A API Quartz

O coração da estrutura é oScheduler. É responsável por gerenciar o ambiente de tempo de execução para nosso aplicativo.

Para garantir a escalabilidade, o Quartz é baseado em uma arquitetura multithread. When started, the framework initializes a set of worker threads que são usados ​​porScheduler para executarJobs.

É assim que a estrutura pode executar muitosJobs simultaneamente. Ele também depende de um conjunto fracamente acoplado de componentes de gerenciamentoThreadPool para gerenciar o ambiente de encadeamento.

As principais interfaces da API são:

  • Scheduler – a API principal para interagir com o planejador do framework

  • Job – uma interface a ser implementada por componentes que desejamos que sejam executados

  • JobDetail – usado para definir instâncias deJobs

  • Trigger – um componente que determina a programação em que um determinadoJob será executado

  • JobBuilder – usado para construir instâncias deJobDetail, que definem instâncias deJobs

  • TriggerBuilder – usado para construir instâncias deTrigger

Vamos dar uma olhada em cada um desses componentes.

4. Agendador

Antes de podermos usarScheduler, ele precisa ser instanciado. Para fazer isso, podemos usar oSchedulerFactory: de fábrica

SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

O ciclo de vida de umScheduler é limitado por sua criação, por meio de umSchedulerFactorye uma chamada para seu métodoshutdown(). Depois de criada, a interfaceScheduler pode ser usada para adicionar, remover e listarJobseTriggers e executar outras operações relacionadas ao agendamento (como pausar um acionador).

No entanto,the Scheduler will not act on any triggers until it has been started with the start() method:

scheduler.start();

5. Jobs

AJob é uma classe que implementa a interfaceJob. Possui apenas um método simples:

public class SimpleJob implements Job {
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        System.out.println("This is a quartz job!");
    }
}

Quando o gatilhoJob’s dispara, o métodoexecute() é invocado por um dos threads de trabalho do planejador.

O objetoJobExecutionContext que é passado para este método fornece a instância do job, com informações sobre seu ambiente de tempo de execução, um identificador paraScheduler que o executou, um identificador paraTrigger que acionou o execução, o objetoJobDetail do trabalho e alguns outros itens.

O objetoJobDetail é criado pelo cliente Quartz no momento em queJob é adicionado aoScheduler. É essencialmente a definição da instância de trabalho:

JobDetail job = JobBuilder.newJob(SimpleJob.class)
  .withIdentity("myJob", "group1")
  .build();

Este objeto também pode conter várias configurações de propriedade paraJob, bem comoJobDataMap, que pode ser usado para armazenar informações de estado para uma determinada instância de nossa classe de trabalho.

5.1. JobDataMap

OJobDataMap é usado para conter qualquer quantidade de objetos de dados que desejamos disponibilizar para a instância do job quando ela for executada. JobDataMap é uma implementação da interface JavaMap e tem alguns métodos de conveniência adicionais para armazenar e recuperar dados de tipos primitivos.

Aqui está um exemplo de colocar dados emJobDataMap ao construirJobDetail, antes de adicionar o trabalho ao planejador:

JobDetail job = newJob(SimpleJob.class)
  .withIdentity("myJob", "group1")
  .usingJobData("jobSays", "Hello World!")
  .usingJobData("myFloatValue", 3.141f)
  .build();

E aqui está um exemplo de como acessar esses dados durante a execução do trabalho:

public class SimpleJob implements Job {
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();

        String jobSays = dataMap.getString("jobSays");
        float myFloatValue = dataMap.getFloat("myFloatValue");

        System.out.println("Job says: " + jobSays + ", and val is: " + myFloatValue);
    }
}

O exemplo acima mostrará "Job diz Olá Mundo! E val é 3,141".

Também podemos adicionar métodos setter à nossa classe de trabalho que corresponde aos nomes das chaves emJobDataMap.

Se fizermos isso, a implementaçãoJobFactory padrão do Quartz automaticamente chama esses setters quando o trabalho é instanciado, evitando assim a necessidade de obter explicitamente os valores do mapa dentro do nosso método execute.

6. Gatilhos

ObjetosTrigger são usados ​​para disparar a execução deJobs.

Quando desejamos agendar umJob, precisamos instanciar um gatilho e ajustar suas propriedades para configurar nossos requisitos de agendamento:

Trigger trigger = TriggerBuilder.newTrigger()
  .withIdentity("myTrigger", "group1")
  .startNow()
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(40)
    .repeatForever())
  .build();

UmTrigger também pode ter umJobDataMap associado a ele. Isso é útil para passar parâmetros paraJob que são específicos para as execuções do gatilho.

Existem diferentes tipos de gatilhos para diferentes necessidades de agendamento. Cada um tem diferentes propriedadesTriggerKey para rastrear suas identidades. No entanto, algumas outras propriedades são comuns a todos os tipos de gatilhos:

  • A propriedadejobKey indica a identidade do trabalho que deve ser executado quando o gatilho dispara.

  • A propriedadestartTime indica quando a programação do acionador entra em vigor pela primeira vez. O valor é um objetojava.util.Date que define um momento no tempo para uma determinada data do calendário. Para alguns tipos de gatilhos, o gatilho é acionado no horário de início especificado. Para outros, simplesmente marca o horário em que o cronograma deve começar.

  • A propriedadeendTime indica quando a programação do gatilho deve ser cancelada.

O quartzo é fornecido com vários tipos de gatilhos diferentes, masthe most commonly used ones are SimpleTrigger and CronTrigger.

6.1. Prioridade

Às vezes, quando temos muitos gatilhos, o Quartz pode não ter recursos suficientes para disparar imediatamente, todos os trabalhos estão programados para disparar ao mesmo tempo. Nesse caso, podemos querer controlar qual dos nossos gatilhos fica disponível primeiro. É exatamente para isso que a propriedadepriority em um gatilho é usada.

For example, quando dez gatilhos são configurados para disparar ao mesmo tempo e apenas quatro threads de trabalho estão disponíveis, os primeiros quatro gatilhos com a prioridade mais alta serão executados primeiro. Quando não definimos uma prioridade em um gatilho, ele usa uma prioridade padrão de cinco. Qualquer valor inteiro é permitido como prioritário, positivo ou negativo.

No exemplo abaixo, temos dois gatilhos com uma prioridade diferente. Se não houver recursos suficientes para disparar todos os gatilhos ao mesmo tempo,triggerA será o primeiro a ser disparado:

Trigger triggerA = TriggerBuilder.newTrigger()
  .withIdentity("triggerA", "group1")
  .startNow()
  .withPriority(15)
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(40)
    .repeatForever())
  .build();

Trigger triggerB = TriggerBuilder.newTrigger()
  .withIdentity("triggerB", "group1")
  .startNow()
  .withPriority(10)
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(20)
    .repeatForever())
  .build();

6.2. Instruções Misfire

Uma falha de ignição ocorre se um gatilho persistentemisses seu tempo de disparo por causa doScheduler sendo desligado, ou no caso de não haver threads disponíveis no pool de threads do Quartz.

Os diferentes tipos de gatilho têm diferentes instruções de falha de ignição disponíveis. Por padrão, eles usam uma instrução de política inteligente. Quando o planejador inicia, ele procura por acionadores persistentes que falharam. Depois disso, ele atualiza cada um deles com base nas instruções de falha de ignição configuradas individualmente.

Vamos dar uma olhada nos exemplos abaixo:

Trigger misFiredTriggerA = TriggerBuilder.newTrigger()
  .startAt(DateUtils.addSeconds(new Date(), -10))
  .build();

Trigger misFiredTriggerB = TriggerBuilder.newTrigger()
  .startAt(DateUtils.addSeconds(new Date(), -10))
  .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withMisfireHandlingInstructionFireNow())
  .build();

Planejamos que o gatilho funcione 10 segundos atrás (por isso, é 10 segundos atrasado no momento em que é criado) para simular uma falha de ignição, por exemplo porque o programador estava inativo ou não tinha uma quantidade suficiente de threads de trabalho disponíveis. Obviamente, em um cenário do mundo real, nunca agendamos gatilhos como esse.

No primeiro gatilho (misFiredTriggerA), nenhuma instrução de manuseio de falha de ignição é definida. Portanto, umsmart policy chamado é usado nesse caso e é chamado:withMisfireHandlingInstructionFireNow(). Isso significa que o trabalho é executado imediatamente após o planejador descobrir o erro de ignição.

O segundo gatilho define explicitamente que tipo de comportamento esperamos quando ocorre falha de ignição. Neste exemplo, é a mesma política inteligente.

6.3. SimpleTrigger

SimpleTrigger is used for scenarios in which we need to execute a job at a specific moment in time. Isso pode ser exatamente uma vez ou repetidamente em intervalos específicos.

Um exemplo pode ser acionar uma execução de trabalho exatamente às 12:20:00 de 13 de janeiro de 2018. Da mesma forma, podemos começar nesse momento e depois mais cinco vezes, a cada dez segundos.

No código abaixo, a datamyStartTime foi definida anteriormente e é usada para construir um gatilho para um carimbo de data / hora específico:

SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
  .withIdentity("trigger1", "group1")
  .startAt(myStartTime)
  .forJob("job1", "group1")
  .build();

A seguir, vamos construir um gatilho para um momento específico no tempo, repetindo a cada dez segundos dez vezes:

SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
  .withIdentity("trigger2", "group1")
  .startAt(myStartTime)
  .withSchedule(simpleSchedule()
    .withIntervalInSeconds(10)
    .withRepeatCount(10))
  .forJob("job1")
  .build();

6.4. CronTrigger

The CronTrigger is used when we need schedules based on calendar-like statements. Por exemplo, podemos especificar horários de disparo, comoevery Friday at noon ouevery weekday at 9:30 am.

As Expressões Cron são usadas para configurar instâncias deCronTrigger. Essas expressões consistem emStrings que são compostas por sete subexpressões. Podemos ler mais sobre Cron-Expressionshere.

No exemplo abaixo, criamos um gatilho que dispara a cada dois minutos entre as 8:00 e as 17:00, todos os dias:

CronTrigger trigger = TriggerBuilder.newTrigger()
  .withIdentity("trigger3", "group1")
  .withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 8-17 * * ?"))
  .forJob("myJob", "group1")
  .build();

7. Conclusão

Neste artigo, mostramos como construir umScheduler para acionar umJob. Também vimos algumas das opções de gatilho mais comuns usadas:SimpleTriggereCronTrigger.

O quartzo pode ser usado para criar agendamentos simples ou complexos para a execução de dezenas, centenas ou até mais tarefas. Mais informações sobre o framework podem ser encontradas nos principaiswebsite.

O código-fonte dos exemplos pode ser encontradoover on GitHub.