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.
¿Cómo cambiar @Scheduled fixedDelay de Spring en tiempo de ejecución?
jsf
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 private
el 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 runtime
y su solución requiere un reinicio?– maldita sea
2 de noviembre de 2017 a las 8:41
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
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
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.
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.
- Implementar Configurador de programación como sugirió cada uno
- Use una solución personalizada como se describe aquí
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.
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