Programaci贸n de tareas de Spring Boot

     

    Introducci贸n

    Programar tareas para que se realicen en una fecha posterior o se repitan en un intervalo fijo es una caracter铆stica muy 煤til. Por ejemplo, los sistemas de boletines o las tareas que procesan informaci贸n en un per铆odo de tiempo determinado dependen de que se programen para ejecutarse en determinados momentos.

    Dado que Spring Boot ofrece varias opciones, las cubriremos e implementaremos todas.

    Configuraci贸n del proyecto

    Como de costumbre, cuando se trata de construir aplicaciones Spring Boot, hacer un proyecto esqueleto es m谩s f谩cil con la ayuda de Spring Initializr. No necesita ninguna dependencia adicional para habilitar la programaci贸n.

    Para habilitar la programaci贸n, todo lo que tenemos que hacer es anotar nuestra clase principal:

    @SpringBootApplication
    @EnableScheduling
    public class SampleScheduleApplication {
        public static void main(String[] args) {
            SpringApplication.run(SampleScheduleApplication.class, args);
        }
    }
    

    los @EnableScheduling La anotaci贸n permite que el contenedor Spring observe cualquier @Scheduled anotaciones en beans gestionados por Spring.

    Programaci贸n condicional

    Hay otra forma de habilitar la programaci贸n: mediante el @ConditionalOnProperty anotaci贸n. Nos permite “encender” y “apagar” nuestras clases de configuraci贸n estableciendo una propiedad en el application.properties clase.

    Para hacer esto, creemos una nueva clase y anot茅mosla con los @EnableScheduling, @Configuration y @ConditionalOnProperty anotaciones:

    @EnableScheduling
    @Configuration
    @ConditionalOnProperty(name = "spring.enable.scheduling")
    public class ScheduleEnabling {}
    

    Ahora, en el application.properties archivo, agreguemos nuestra nueva propiedad y configur茅mosla en true:

    spring.enable.scheduling = true
    

    Con solo cambiar esta variable, podemos activar y desactivar la funcionalidad.

    Para ejecutar un m茅todo en un horario, necesita anotarlo con el @Scheduled anotaci贸n. Cambiar los par谩metros de la anotaci贸n definir谩 si es a una tasa fija, con un retraso fijo, intervalos personalizados, etc.

    Programaci贸n con FixedRate y FixedRateString

    Para programar un m茅todo para que se ejecute a una tasa fija, agregaremos el par谩metro adecuado a nuestra anotaci贸n: @Scheduled(fixedRate). Este par谩metro acepta enteros, expresados 鈥嬧媏n milisegundos. Entonces, si desea una tasa de 1 segundo, la tasa debe ingresarse como 1000 ya que el valor es milisegundos.

    Alternativamente, puede utilizar el @Scheduled(fixedRateString) par谩metro para externalizar la cantidad utilizando una variable de cadena de entorno.

    Para evitar confusiones, iremos con el fixedRateString, que es esencialmente un par谩metro de cadena que especifica la tasa, en lugar del valor entero. Puede resultar un poco complicado intentar que un m茅todo se repita mensualmente en milisegundos.

    Establezcamos una variable en el application.properties archivo:

    sample.schedule.string = PT2S
    

    El prefijo PT es el est谩ndar ISO-8601 para anotando duraciones o per铆odos, y con esto, podemos llamar al sample.schedule.string para programar una llamada a un m茅todo en 2 segundos.

    Esto tambi茅n nos permite especificar diferentes retrasos para diferentes perfiles en uso:

    @Scheduled(fixedRateString = "${sample.schedule.string}")
    public void scheduleTaskWithFixedRate() throws InterruptedException {
        task1();
        task2();
    }
    
    public void task1() throws InterruptedException {
        logger.info("Task 1 starts" + Thread.currentThread());
        Thread.sleep(1000);
        logger.info("Task 1 ends" + Thread.currentThread());
    }
    
    public void task2() throws InterruptedException {
        logger.info("Task 2 starts" + Thread.currentThread());
        Thread.sleep(1000);
        logger.info("Task 2 ends" + Thread.currentThread());
    }
    

    Ejecutemos este fragmento de c贸digo:

    Sin embargo, podemos ver un problema aqu铆:

    La tarea 1 comienza a las 00:01:44 y finaliza a las 00:01:45, como se esperaba.
    La tarea 2 comienza a las 00:01:45 y termina a las 00:01:46, como se esperaba.
    La tarea 1 comienza a las 00:01:46 y termina a las 00:01:47.

    Ya que ambos task1() y task2() se han ejecutado, esperar铆a que el m茅todo esperara 2 segundos adicionales para ejecutarse nuevamente.

    Las tareas de tasa fija no esperan la finalizaci贸n de la ejecuci贸n anterior, simplemente invoca el m茅todo a una tasa espec铆fica. Dado que se necesitan 2 segundos para terminar los m茅todos task1() y task2(), el m茅todo se invoca nuevamente al mismo tiempo que estos dos terminan.

    Esto puede convertirse en un problema en un entorno con m煤ltiples tareas programadas.

    Considera esto – task1() tarda 1 minuto en completarse, y task2() tarda 5 segundos en completarse. Dado que ambos se ejecutan en un solo hilo, puede haber una situaci贸n que task1() comienza a procesar y bloquear谩 el hilo. Esto no permitir谩 task2() procesar incluso si hay un fixedRate de 5 segundos.

    En este escenario, necesitamos aumentar la cantidad de subprocesos que est谩n disponibles en nuestro grupo de subprocesos para la programaci贸n. Spring proporciona una propiedad que podemos manipular para especificar el tama帽o: spring.task.scheduling.pool.size – el valor predeterminado es 1.

    Podemos usar una tasa fija cuando una tarea en particular debe realizarse repetidamente, pero cada tarea es independiente de la otra. Adem谩s, tenga cuidado de no tener tareas pesadas sin un ritmo adecuado, ya que las tareas incompletas pueden provocar una OutOfMemoryError.

    Programaci贸n con FixedDelay y FixedDelayString

    UN fixedDelay funciona muy similar a un fixedRate. Pero la diferencia aqu铆 es que la demora fija espera hasta la finalizaci贸n de la ejecuci贸n anterior para iniciar la siguiente. Imagine un escenario en el que su funci贸n tarda 1 segundo en completarse y le ha dado un retraso fijo de 2 segundos.

    Esto, a su vez, dar谩 como resultado un total de 3 segundos.

    En los registros a continuaci贸n, puede ver claramente que la diferencia entre las dos tareas posteriores es de 3 segundos. Esto incluye el tiempo de retardo fijo de 1 segundo, as铆 como los 2 segundos que hemos dado como suspensi贸n:

    @Scheduled(fixedDelayString = "${sample.schedule.string}")
    public void scheduleTaskWithFixedDelay() throws InterruptedException {
        task1();
    }
    
    public void task1() throws InterruptedException {
        logger.info("Task 1 starts" + Thread.currentThread());
        Thread.sleep(1000);
        logger.info("Task 1 ends" + Thread.currentThread());
    }
    

    Ejecutemos este fragmento de c贸digo:

    Hay un par谩metro adicional que se puede agregar a las tareas programadas, y es el initialDelay.

    Este no requiere mucha explicaci贸n ya que se usa junto con los dos anteriores. La demora inicial, como su nombre sugiere con raz贸n, proporciona la demora inicial para la primera ejecuci贸n.

    Si tiene una funci贸n con un retraso inicial de 2 segundos y una frecuencia fija de 1 segundo, la primera ejecuci贸n se retrasar谩 2 segundos y la funci贸n se ejecutar谩 cada segundo despu茅s:

    @Scheduled(initialDelay = 1000, fixedRateString = "${sample.schedule.string}")
    public void scheduleTaskWithInitialDelay() throws InterruptedException {
        task1();
    }
    

    Tambi茅n podemos optar por utilizar un initialDelayString que nos permite externalizar el valor de retardo.

    Intervalos de tiempo personalizados

    La tasa fija y el retardo fijo son los par谩metros m谩s utilizados para la programaci贸n y las cadenas de retardo nos permiten externalizar los valores y hacerlos configurables.

    Pero hasta ahora solo hemos visto ejemplos muy gen茅ricos de tarifas. Puede haber una situaci贸n en la que necesitemos intervalos de tiempo muy espec铆ficos. He aqu铆, costumbre cron Expresiones

    Expresiones Cron

    La mayor铆a de los desarrolladores probablemente hayan o铆do hablar cron utilidad en Linux. Es un proceso demonio que se ejecuta sin la necesidad de la intervenci贸n del usuario y ejecuta tareas.

    La sintaxis de las expresiones cron en el cron La utilidad y la sintaxis de las expresiones cron para la programaci贸n son en su mayor铆a similares.

    Las expresiones cron son b谩sicamente cadenas que describen los detalles del programa. Proporciona mucho m谩s control que los 2 m茅todos anteriores:

    Nombre Requerido Valores permitidos Caracteres especiales permitidos

    Segundossi0-59, - * /
    Minutossi0-59, - * /
    Horassi0-23, - * /
    Dia del messi1-31, - * / L W C
    Messi0-11 o ENE-DIC, - * /
    D铆a de la semanasi1-7 o SUN-SAT, - * / L C #
    A帽oNovac铆o o 1970-2099, - * /

    La tabla anterior especifica los valores requeridos, los valores permitidos y los caracteres especiales para una expresi贸n cron.

    Las expresiones cron pueden ser muy simples, pero tambi茅n muy complejas. Una comprensi贸n clara de los valores har谩 que sea m谩s f谩cil jugar con ellos.

    Excepto por el campo del a帽o, todos los dem谩s campos son obligatorios:

    <second> <minute> <hour> <day-of-month> <month> <day-of-week> <year>
    

    Ejemplo: 0 0 12 * * ? 2019 – Esta expresi贸n cron se activa a las 12 p.m., todos los d铆as del mes, para cada mes, para el a帽o 2019.

    Para algunos valores comunes, tambi茅n puede usar las anotaciones predefinidas:

    • @reboot: Programe el m茅todo para cada reinicio de la aplicaci贸n
    • @yearly/@anually: Programe el m茅todo para que se ejecute una vez al a帽o
    • @monthly: Programe el m茅todo para que se ejecute una vez al mes
    • @weekly: Programe el m茅todo para que se ejecute una vez a la semana
    • @daily/@midnight: Programe el m茅todo para que se ejecute una vez al d铆a
    • @hourly: Programe el m茅todo para que se ejecute una vez cada hora

    Escribamos un ejemplo de c贸digo para esto:

    @Scheduled(cron="0 0 12 * * ? 2019")
    public void doSomething() {
        // Something
    }
    

    Una cosa importante a tener en cuenta al programar son las zonas horarias, la pesadilla de todo desarrollador que trabaja con el tiempo.

    Es probable que desee configurar el zone bandera a una regi贸n espec铆fica. Por ejemplo, ejecutaremos este m茅todo a las 12 p.m., todos los d铆as de 2019, seg煤n la zona horaria de Par铆s:

    @Scheduled(cron="0 0 12 * * ? 2019", zone="Europe/Paris")
    public void doSomething() {
        // Something
    }
    

    Puede encontrar todas las zonas horarias en la documentos oficiales de Oracle.

    Por supuesto, tambi茅n puede externalizar expresiones cron a trav茅s del application.properties archivo:

    cron.expression= 0 0 12 * * ? 2019
    

    Y luego invocarlo a trav茅s de:

    @Scheduled(cron="${cron.expression}", zone="Europe/Paris")
    public void doSomething() {
        // Something
    }
    

    Tambi茅n puede utilizar un sitio como FreeFormatter para generar una expresi贸n cron estableciendo los par谩metros de entrada. Esto es muy 煤til para aquellos que son nuevos en la creaci贸n de expresiones cron.

    Conclusi贸n

    En este art铆culo, hemos visto c贸mo podemos programar tareas con Spring Boot. La mayor ventaja de usar Spring Boot es la facilidad con la que podemos implementar la programaci贸n. No solo eso, tambi茅n ofrece varias opciones para que podamos elegir la que se adapte a nuestras necesidades.

    Los programadores son componentes esenciales de la mayor铆a de las aplicaciones porque env铆an informaci贸n de tiempo cr铆tico y espec铆fica del usuario cuando es necesario. 隆Ahora sabes c贸mo!

    .

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *