Tarea programada JAVA @Scheduled && resuelve el problema de ejecutar una tarea programada varias veces @SchedulerLock bloqueo

Registre el uso de @Scheduled para configurar tareas programadas en proyectos JAVA y use @SchedulerLock para bloquear y resolver el problema de ejecuciones múltiples de tareas programadas en implementaciones de múltiples instancias.

@Tareas programadas programadas

@Scheduled se puede configurar y usar directamente en la función requerida, lo cual es súper simple y conveniente.

Instrucciones

  1. @Scheduled(fixedDelay = 1000)
    El intervalo de tiempo entre el final de la tarea anterior y el inicio de la siguiente se fija en 1 segundo. La ejecución de la tarea siempre espera hasta que finalice la ejecución de la tarea anterior antes de ejecutarse. La unidad aquí es milisegundos, por lo que es 1000
  2. @Scheduled(fixedRate = 1000)
    Las tareas se ejecutan cada 1 segundo (si la tarea tarda más de 1 segundo, la siguiente tarea se ejecuta inmediatamente después de que finalice la tarea anterior).
  3. @Scheduled(fixedDelay = 1000, initialDelay = 2000)
    La primera tarea ejecutada por inicialDelay se retrasará 2 segundos antes de comenzar.
  4. @Scheduled(cron = “0 * * * * ?”)
    Expresión cron, ejecutada cada minuto.

expresión cron

Herramienta en línea de expresión cron, seleccione "Java (Quartz)" como tipo: https://tool.lu/crontab/

La expresión cron tiene la forma de una cadena. La cadena está separada por 5 o 6 espacios y dividida en 6 o 7 campos. Cada campo representa un significado. Cron tiene los siguientes dos formatos de sintaxis:

(1) Segundos Minutos Horas Día del mes Mes Día de la semana Año
(2) Segundos Minutos Horas Día del mes Mes Día de la semana
Insertar descripción de la imagen aquí
(1) *: Indica cualquier valor que coincida con este campo. Si se usa en el campo Minutos, significa que el evento se activará cada minuto.

(2)?: Sólo se puede utilizar en los campos Día del mes y Día de la semana. También coincide con cualquier valor del dominio, pero no es así. Porque DayofMonth y DayofWeek se afectarán entre sí. Por ejemplo, si desea activar la programación el día 20 de cada mes, sin importar en qué día de la semana caiga el día 20, solo puede usar la siguiente escritura: 13 13 15 20?, donde solo se puede usar el último dígito ? , pero no se puede utilizar. Si se utiliza *, significa que se activará independientemente del día de la semana. En realidad, este no es el caso.

(3)-: Indica el rango. Por ejemplo, usar 5-20 en el campo Minutos significa que se activa cada minuto desde 5 minutos hasta 20 minutos.

(4) /: Indica que el disparador comienza a la hora de inicio y luego se dispara a una hora fija. Por ejemplo, usar 5/20 en el campo Minutos significa que se activa una vez cada 5 minutos y una vez cada 25, 45, etc.

(5): Indica enumerar valores de enumeración. Por ejemplo: usar 5,20 en el campo Minutos significa que se activa cada minuto a las 5 y 20 minutos.

(6) L: indica el final y solo puede aparecer en los campos Día de la semana y Día del mes. Si usa 5L en el campo Día de la semana, significa que se activará el último jueves.

(7) W: indica un día hábil válido (de lunes a viernes), que solo puede aparecer en el campo Día del mes, el sistema activará el evento en el día hábil válido más cercano a la fecha especificada. Por ejemplo: usando 5W en Día del Mes, si el día 5 es sábado, se activará el día laborable más cercano: el viernes, que es el día 4. Si el día 5 es domingo, se activará el día 6 (lunes); si el día 5 es uno de los días de lunes a viernes, se activará el día 5. Otro punto, la búsqueda más reciente de W no durará meses.

(8) LW: Estos dos caracteres se pueden utilizar juntos para indicar el último día laborable de un determinado mes, es decir, el último viernes.

(9) #: Se utiliza para determinar el día de la semana de cada mes y solo puede aparecer en el campo Día del mes. Por ejemplo, en 4#2, significa el segundo miércoles de un mes determinado.

Ejemplo de expresión cron:
Ejecutar cada 5 segundos: /5 * ?
Ejecutar cada 1 minuto: 0 /1 ?
Ejecutar una vez al día a las 23:00: 0 0 23 ?
Ejecutar una vez al día a la 1 am: 0 0 1 ?
Cada mes Ejecutado una vez a la 1 am del día 1: 0 0 1 1 * ?
Ejecutado una vez a las 23:00 el último día de cada mes: 0 0 23 L * ?
Ejecutado una vez cada domingo a la 1 am: 0 0 1 ? * L

@SchedulerLock bloqueo

Si se encuentra con la situación de múltiples instancias, lo que provocará múltiples ejecuciones de tareas programadas, puede usar bloqueos para resolver este problema @SchedulerLock

shedlock proporciona implementaciones de bloqueo para mongo, redis, zookeeper, jdbc, etc. Aquí se utiliza jdbc.

Pasos para el uso

1. Introduzca dependencias relevantes en pom.xml

       <dependency>
			<groupId>net.javacrumbs.shedlock</groupId>
			<artifactId>shedlock-spring</artifactId>
			<version>4.42.0</version>
		</dependency>

		<dependency>
			<groupId>net.javacrumbs.shedlock</groupId>
			<artifactId>shedlock-provider-jdbc-template</artifactId>
			<version>4.42.0</version>
		</dependency>

2. Cree una tabla shedlock en la base de datos.

CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until TIMESTAMP(3) NOT NULL,
    locked_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name));

El nombre del bloqueo declarado por @SchedulerLock crea automáticamente el par clave-valor correspondiente para proporcionar el bloqueo.
@SchedulerLock(name = “xx”)El valor del nombre corresponde a la asignación de nombre en la biblioteca.
Cuando se ejecuta la tarea programada, habrá información de bloqueo en la tabla shedlock. Si la tabla está vacía, significa que la configuración no ha tenido efecto. Debe verificar su propia configuración.

Descripción del atributo:
nombre nombre de bloqueo, el nombre debe ser la clave principal
lock_hasta que se libere el tiempo de bloqueo
bloqueado_en el momento de adquirir el bloqueo
bloqueado_por el proveedor de bloqueo

3. Clase de configuración

package com.xx.config;

import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;

import javax.sql.DataSource;
import java.time.Duration;

@Configuration
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "10m")
public class ShedlockConfig {
    
    
    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
    
    
        return new JdbcTemplateLockProvider(
                JdbcTemplateLockProvider.Configuration.builder()
                        .withJdbcTemplate(new JdbcTemplate(dataSource))
                        .usingDbTime() // Works on Postgres, MySQL, MariaDb, MS SQL, Oracle, DB2, HSQL and H2
                        .build()
        );
    }
}

4. Agregue las anotaciones correspondientes a la función de tarea requerida.

Por ejemplo: si la configuración aquí se ejecuta cada 10 minutos, el bloqueo se mantendrá durante 5 minutos y se liberará después de 5 minutos; cuando el nodo sea anormal o muera, el bloqueo se liberará automáticamente después de 15 minutos de forma predeterminada.
En circunstancias normales, ShedLock libera el bloqueo inmediatamente después de completar la tarea. Los valores predeterminados también se proporcionan en @EnableSchedulerLock. También podemos simplemente completar el nombre directamente. Si se configura aquí, la configuración predeterminada se sobrescribirá.

	@Scheduled(cron = "0 0 * * * ?")
    @SchedulerLock(name = "test",
            lockAtMostFor = 15 * 60 * 1000, lockAtLeastFor = 5 * 60 * 1000)
    public void test(){
    
    
        log.info(String.format("EXECUTE:%s",true));
    }

lockAtMostFor en milisegundos
lockAtMostForString Usar "PT14M" significa que estará bloqueado por no más de 14 minutos.

lockAtLeastFor unidad milisegundos
lockAtLeastForString use "PT14M" o "PT30S"

Descripción del parámetro de anotación:
● nombre: nombre del bloqueo. El nombre del bloqueo debe ser único y sólo se puede ejecutar una tarea a la vez.
● lockAtMostFor: especifica cuánto tiempo se debe retener el bloqueo cuando el nodo de ejecución muere. Esta es solo una opción alternativa; en circunstancias normales, el bloqueo se libera tan pronto como se completa la tarea. Debe usarse estableciendo lockAtMostFor en un valor que sea mucho más largo que el tiempo de ejecución normal. Si una tarea tarda más que lockAtMostFor, el comportamiento resultante puede ser impredecible (más procesos mantendrán el bloqueo de manera efectiva).
● lockAtLeastFor: el tiempo mínimo para retener el bloqueo para evitar que se repita la ejecución programada si hay diferencias en la hora del servidor entre diferentes nodos si el tiempo de ejecución programada es corto.

hilo

De forma predeterminada, todas las tareas programadas se ejecutan en un hilo. En otras palabras, la tarea programada 1 se ha estado ejecutando y la tarea programada 2 ha estado esperando a que se complete la tarea programada 1. Para evitar esperarse unos a otros, puede configurar un grupo de subprocesos para tareas programadas.

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
    
    
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    
    
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(50));
    }
}

Una vez que se inicia el programa, se iniciarán gradualmente 50 subprocesos y se colocarán en el grupo de subprocesos. Cada tarea programada ocupará 1 hilo. Pero la misma tarea programada todavía se ejecuta en el mismo hilo.

También se puede configurar la ejecución asincrónica y las mismas tareas no se afectarán entre sí.

Clase de configuración

@Configuration
@EnableAsync
public class ScheduleConfig {
    
    
    @Bean
    public TaskScheduler taskScheduler() {
    
    
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(50);
        return taskScheduler;
    }
}

Simplemente agregue la anotación @Async al método y la misma tarea programada también iniciará varios subprocesos para su procesamiento.

	@Async
	@Scheduled(cron = "0 0 * * * ?")
    @SchedulerLock(name = "test",
            lockAtMostFor = 15 * 60 * 1000, lockAtLeastFor = 5 * 60 * 1000)
    public void test(){
    
    
        log.info(String.format("EXECUTE:%s",true));
    }

Referencia: https://github.com/lukas-krecan/ShedLock
https://blog.csdn.net/reinsunshine/article/details/126094351

Supongo que te gusta

Origin blog.csdn.net/weixin_44436677/article/details/127581462
Recomendado
Clasificación