O Quarkus possui duas formas diferentes de agendar atividades, no post de hoje, vamos aprender uma forma mais simples que é para as atividades que ocorrem repetidamente em um ambiente não paralelizado.
Introdução
Não faltam exemplos sobre diferentes aplicações que utilizam atividades repetidas. Seja algum aplicativo que verifica se chegou um e-mail ou mesmo aquela ̶g̶a̶m̶b̶i̶a̶r̶r̶a̶ solução alternativa que precisa reiniciar a aplicação de tempos em tempos por algum vazamento de memória desconhecido (atire a primeira pedra que nunca fez isso).
Não importa o motivo, em algum momento, você acabará encontrando situações onde uma determinada ação precisa ser repetida de tempos em tempos.
O que faremos
No exemplo de hoje, vamos fazer um pequeno servidor que escreve sons de golpes como soc, tum, pow de tempos em tempos.
A tabela abaixo mostra/explica qual será a regra utilizada para que cada golpe apareça.
Nome do golpe | Frequência | Delay |
---|---|---|
Soc | A cada 11 segundos | 0 segundos |
Tum | A cada segundo múltiplo de 17 | 0 segundos |
Pow | A cada segundo múltiple de 23 | 0 segundos |
Kabam | A cada 29 segundos | 0 segundos |
kataplow | A cada 3 segundos | 1 minuto |
Hora da ação
Para criar um novo projeto Quarkus já com a extensão que controla as tarefas repetitivas basta usar o seguinte código.
mvn io.quarkus:quarkus-maven-plugin:1.6.0.Final:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=scheduler-soc-tum-pow \
-DclassName="org.acme.schedule.SocTumPowGenerator" \
-Dextensions="scheduler"
Isso vai criar uma pasta chamada scheduler-soc-tum-pow que terá todo o nosso código e já virá com a extensão scheduler instalada.
Depois disso, basta deletar os testes já gerados.
Com isso, vamos abrir o arquivo SocTumPowGenerator.java e editar para ficar com o seguinte código.
package org.acme.schedule;
import javax.enterprise.context.ApplicationScoped;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.quarkus.scheduler.Scheduled;
import io.quarkus.scheduler.ScheduledExecution;
import java.util.concurrent.TimeUnit;
@ApplicationScoped // (1)
public class SocTumPowGenerator {
private static final Logger log
= LoggerFactory.getLogger(SocTumPowGenerator.class); // (2)
@Scheduled(every = "11s") // (3)
void soc() {
log.info("soc");
}
@Scheduled(cron = "0/17 * * * * ?") // (4)
void tum() {
log.info("tum");
}
@Scheduled(cron = "{cron.expr}") // (5)
void pow() {
log.info("pow");
}
@Scheduled(every = "{every.expr}") // (6)
void kabam() {
log.info("kabam");
}
@Scheduled(delay = 1, // (7)
delayUnit = TimeUnit.MINUTES, // (8)
every = "3s") // (9)
void kataplow() {
log.info("kataplow");
}
}
e no arquivo application.properties, colocar as seguintes propriedades.
quarkus.log.console.format=%d{HH:mm:ss}: %s%e%n # (10)
cron.expr=0/23 * * * * ? # (11)
every.expr=29s # (12)
Pequena nota: d̶ó̶ no arquivo application.properties é necessário retirar o jogo da velha e os parênteses, se não ocorrerá erros.
Agora vamos destrinchar o que cada uma das linhas faz.
- Todo código que executa um cron, precisa ser um bean gerenciável. Adicionar a anotação ApplicationScoped é o melhor jeito de fazer isso. Além disso, essa anotação garante que será criado apenas um único agendador de tarefas;
- Usei uma biblioteca de log para facilitar a escrita, pois ela mostrará o horário que o método foi executado;
- A anotação Scheduled informa que o método será repetido de tempos em tempos. O atributo every informa de quanto em quanto tempo esse método será chamado;
- O atributo cron permite que o controle de tempos seja feito baseado no padrão usado pelo crontab;
- O Quarkus possui uma integração muito legal com a parte de configuração. Usando essas chaves, ele vai buscar as informações no arquivo application.properties;
- O mesmo vale para expressões do tipo every;
- Instruções repetidas do tipo every começam a rodar assim que possível. Dessa forma, mesmo que você tenha algo do tipo every="40m", a primeira execução ocorrerá logo após o servidor inicializar e a execução seguinte ocorrerá 40 minutos depois da primeira execução. É possível atrasar a primeira chamada do método através do atributo delay;
- O atributo delay informa o tempo que a execução vai atrasar. O atributo delayUnit informa a unidade que de tempo que vai atrasar;
- De forma semelhante ao item 3, aqui informamos que a operação ocorrerá a cada 3 segundos;
- Configuração de log para imprimir apenas o horário e o texto do log;
- Expressão cron utilizada para o item 5 e inserida como configuração;
- Expressão delay utilizada para o item 6 e inserida como configuração.
Executando o código com essas configurações, a saída mais ou menos como essa:
mvn quarkus:dev
executing mvnw instead of mvn
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------< org.acme:code-with-quarkus >---------------------
[INFO] Building code-with-quarkus 1.0.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- quarkus-maven-plugin:1.6.0.Final:dev (default-cli) @ code-with-quarkus ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /home/aleatorio/VisualCodeProjects/blog-posts-code/scheduled-task/target/classes
Listening for transport dt_socket at address: 5005
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
22:37:00: code-with-quarkus 1.0.0-SNAPSHOT on JVM (powered by Quarkus 1.6.0.Final) started in 0.772s.
22:37:00: Profile dev activated. Live Coding activated.
22:37:00: Installed features: [cdi, scheduler]
22:37:01: kabam
22:37:01: soc
22:37:12: soc
22:37:17: tum
22:37:23: pow
22:37:23: soc
22:37:30: kabam
22:37:34: soc
22:37:34: tum
22:37:45: soc
22:37:46: pow
22:37:51: tum
22:37:56: soc
22:37:59: kabam
22:38:00: kataplow
22:38:00: pow
22:38:00: tum
22:38:03: kataplow
22:38:06: kataplow
22:38:07: soc
22:38:09: kataplow
22:38:12: kataplow
22:38:15: kataplow
22:38:17: tum
22:38:18: kataplow
22:38:18: soc
22:38:21: kataplow
22:38:23: pow
22:38:24: kataplow
Considerações
Para tarefas simples e repetidas esse processo é mesmo uma mão na roda. Simplifica tudo. Prefiro trabalhar com os parâmetros definidos no arquivo de configuração, pois posso alterar tudo conforme a necessidade do meu teste.
Ainda estou me acostumando com essa questão de escrever artigos, espero que consiga ajudar mais e mais pessoas com isso.
Ah, e o código de hoje pode ser encontrado no github.
Top comments (2)
Muito bom Lucas! Parabéns!
Muito obrigado =D