¿Cómo cambiar @Scheduled fixedDelay de Spring en tiempo de ejecución?

6 minutos de lectura

avatar de usuario
jsf

Tengo el requisito de ejecutar un trabajo por lotes en un intervalo fijo y tengo la capacidad de cambiar la hora de este trabajo por lotes en tiempo de ejecución. Por esto me encontré @Scheduled anotación proporcionada bajo el marco Spring. Pero no estoy seguro de cómo cambiaría el valor de fixedDelay en tiempo de ejecución. Busqué en Google pero no encontré nada útil.

  • Veo que aceptaste la mejor respuesta, pero aún veo que había algunos problemas sin resolver. ¿Se resolvió el problema de NPE? ¿Es posible que publiques la solución completa para esto? Salud

    – déspota

    22 de noviembre de 2013 a las 9:57

  • Posible duplicado de Programación de un trabajo con Spring mediante programación (con tasa fija establecida dinámicamente)

    – Steve Cámaras

    5 de octubre de 2015 a las 9:54

  • @jsf puedes ver mi respuesta aquí: stackoverflow.com/a/51333059/2590960

    – grep

    13 de julio de 2018 a las 23:36

avatar de usuario
bohemio

¡En Spring Boot, puede usar una propiedad de la aplicación directamente!

Por ejemplo:

@Scheduled(fixedDelayString = "${my.property.fixed.delay.seconds}000")
private void process() {
    // your impl here
}

Tenga en cuenta que también puede tener un valor predeterminado en caso de que la propiedad no esté definida, por ejemplo, para tener un valor predeterminado de “60” (segundos):

@Scheduled(fixedDelayString = "${my.property.fixed.delay.seconds:60}000")

Otras cosas que descubrí:

  • el método debe ser nulo
  • el método no debe tener parámetros
  • el método puede ser private

Encontré ser capaz de usar private visibilidad útil y lo usé de esta manera:

@Service
public class MyService {
    public void process() {
        // do something
    }

    @Scheduled(fixedDelayString = "${my.poll.fixed.delay.seconds}000")
    private void autoProcess() {
        process();
    }
}

Siendo privateel método programado puede ser local para su servicio y no formar parte de la API de su Servicio.

Además, este enfoque permite que el process() método para devolver un valor, que un @Scheduled el método puede que no. por ejemplo, tu process() El método puede parecerse a:

public ProcessResult process() {
    // do something and collect information about what was done
    return processResult; 
}

para proporcionar alguna información sobre lo que sucedió durante el procesamiento.

  • Gracias, fixedDelayString es lo que estaba buscando

    – bastante vacío

    3 de febrero de 2016 a las 9:13

  • Gran respuesta. Funciona como se describe.

    – destello

    06/09/2016 a las 11:45

  • @Bohemain Gracias por la solución, pero ¿cómo se actualiza fixedDelay en tiempo de ejecución?

    – Bancos de arcilla

    27 de septiembre de 2016 a las 2:53

  • @KuraiBankusu está establecido en puesta en marcha tiempo mediante el uso de un archivo de configuración/propiedades específico del entorno. No puede cambiarlo después del inicio, pero rara vez hay un caso de uso para eso. Si realmente necesita cambiarlo después del inicio, ¡cambie la configuración y reinicie!

    – bohemio

    27 de septiembre de 2016 a las 2:56


  • Esta no es una respuesta útil. El OP pidió at runtimey su solución requiere un reinicio?

    – maldita sea

    2 de noviembre de 2017 a las 8:41


avatar de usuario
ach

Puedes usar un Trigger para establecer dinámicamente el próximo tiempo de ejecución.

Consulte mi respuesta a Programación de un trabajo con Spring programáticamente para obtener más detalles.

  • FYI – Te dejé un comentario en NullPointerException error notado en el código.

    – jsf

    6 de marzo de 2013 a las 17:03

  • ¿Hay alguna manera de interrumpir el Trigger actual y cambiar su valor mientras está durmiendo?

    – jsf

    6 de marzo de 2013 a las 17:53

  • También puedes ver mi respuesta: stackoverflow.com/a/51333059/2590960

    – grep

    13 de julio de 2018 a las 23:34

avatar de usuario
grep

crear interfaz, algo así:

    public abstract class DynamicSchedule{
        /**
         * Delays scheduler
         * @param milliseconds - the time to delay scheduler.
         */
        abstract void delay(Long milliseconds);

        /**
         * Decreases delay period
         * @param milliseconds - the time to decrease delay period.
         */
        abstract void decreaseDelayInterval(Long milliseconds);

        /**
         * Increases delay period
         * @param milliseconds - the time to increase dela period
        */
        abstract void increaseDelayInterval(Long milliseconds);
}

A continuación, implementemos la interfaz Trigger que se encuentra en org.springframework.scheduling en el proyecto spring-context.

import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;

import java.util.Date;
import java.util.concurrent.ScheduledFuture;

public class CustomDynamicSchedule extends DynamicSchedule implements Trigger {

    private TaskScheduler taskScheduler;
    private ScheduledFuture<?> schedulerFuture;

    /**
     * milliseconds
     */
    private long delayInterval;

    public CustomDynamicSchedule(TaskScheduler taskScheduler) {
        this.taskScheduler = taskScheduler;
    }


    @Override
    public void increaseDelayInterval(Long delay) {
        if (schedulerFuture != null) {
            schedulerFuture.cancel(true);
        }
        this.delayInterval += delay;
        schedulerFuture = taskScheduler.schedule(() -> { }, this);
    }

    @Override
    public void decreaseDelayInterval(Long delay) {
        if (schedulerFuture != null) {
            schedulerFuture.cancel(true);
        }
        this.delayInterval -= delay;
        schedulerFuture = taskScheduler.schedule(() -> { }, this);
    }

    @Override
    public void delay(Long delay) {
        if (schedulerFuture != null) {
            schedulerFuture.cancel(true);
        }
        this.delayInterval = delay;
        schedulerFuture = taskScheduler.schedule(() -> { }, this);
    }

    @Override
    public Date nextExecutionTime(TriggerContext triggerContext) {
        Date lastTime = triggerContext.lastActualExecutionTime();
        return (lastTime == null) ? new Date() : new Date(lastTime.getTime() + delayInterval);
    }
}

ahora configuración:

@Configuration
public class DynamicSchedulerConfig {
    @Bean
    public CustomDynamicSchedule getDynamicScheduler() {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.initialize();
        return  new CustomDynamicSchedule(threadPoolTaskScheduler);
    }
}

y uso:

@EnableScheduling
@Component
public class TestSchedulerComponent {

    @Autowired
    private CustomDynamicSchedule dynamicSchedule;

    @Scheduled(fixedDelay = 5000)
    public void testMethod() {
        dynamicSchedule.delay(1000l);
        dynamicSchedule.increaseDelayInterval(9000l);
        dynamicSchedule.decreaseDelayInterval(5000l);
    }

}

  • ¿Qué significa esta sintaxis? () -> { }

    – Aliuk

    12 de septiembre de 2018 a las 10:12

  • La respuesta a mi comentario está en esta otra pregunta: stackoverflow.com/questions/52292953/meaning-of-lambda-in-java

    – Aliuk

    12/09/2018 a las 10:47

  • puedes pasar la clase ejecutable. Por ejemplo, puede pasar la clase que registrará cuándo se cambiará la hora.

    – grep

    12/09/2018 a las 10:55

  • @grep: usé tu respuesta, pero no me funciona. Imprimí: System.out.println(last time);, siendo nulo.

    – mayank bisht

    22 de mayo de 2019 a las 7:40


  • @grep: Su código no funciona para mí, ¿podría ayudarme?

    – mayank bisht

    26 de mayo de 2019 a las 6:33

avatar de usuario
Sagar Ahuja

También puede usar Spring Expression Language (SpEL) para esto.

@Scheduled(fixedDelayString = "#{@applicationPropertyService.getApplicationProperty()}")
public void getSchedule(){
   System.out.println("in scheduled job");
}

@Service
public class ApplicationPropertyService {

    public String getApplicationProperty(){
        //get your value here
        return "5000";
    }
}

AFAIK, la API de Spring no le permitirá acceder a las partes internas que necesita para cambiar el disparador. Pero en su lugar, podría configurar manualmente los beans:

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
    <property name="jobDetail" ref="jobDetail" />
    <property name="startDelay" value="10000" />
    <property name="repeatInterval" value="50000" />
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="simpleTrigger" />
        </list>
    </property>
</bean>

Luego, como se documenta en SchedulerFactoryBean:

Para el registro dinámico de trabajos en tiempo de ejecución, use una referencia de bean a este SchedulerFactoryBean para obtener acceso directo al Programador de cuarzo (org.quartz.Scheduler). Esto le permite crear nuevos trabajos y disparadores, y también controlar y monitorear todo el Programador.

avatar de usuario
egelev

He lidiado con el mismo problema. Teníamos el requisito de cambiar la expresión cron en tiempo de ejecución y reprogramar el servicio. Entonces debería haber:

  • sin recopilación
  • sin redistribución
  • sin reiniciar

de la aplicación. Inspeccioné todas las soluciones populares, pero solo 2 de ellas cumplen todos los requisitos.

La desventaja del enfoque SchedulingConfigurer es que es basado en tirar, es decir, la configuración de programación se extrae cada vez que se ejecuta la lógica empresarial del servicio. Esto no es algo malo en general, pero si la configuración se cambia con poca frecuencia y el intervalo de ejecución es corto, habrá muchas solicitudes innecesarias.

La desventaja de la solución personalizada es que requiere un poco más de codificación pero es basado en push y reacciona a los cambios de configuración para que no se realicen solicitudes/llamadas innecesarias.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad