2015-05-18 21 views
7

sto provando ad eseguire un'attività a una velocità fissa usando l'annotazione @Scheduled in java spring. tuttavia, sembra che per impostazione predefinita spring non esegua un'attività fixedRate a una velocità fissa se l'attività è più lenta della velocità. c'è qualche impostazione che posso aggiungere alla mia configurazione di primavera per cambiare questo comportamento?come è possibile configurare Spring per eseguire attività FixedRate sovrapposte?

esempio:

@Service 
public class MyTask{ 
    @Scheduled(fixedRate = 1000) 
    public void doIt(){ 
     // this sometimes takes >1000ms, in which case the next execution is late 
     ... 
    } 
} 

ho un work-around, ma sembra tutt'altro che ideali. Fondamentalmente, io basta sostituire l'esecutore di default single-thread con un pool di thread, quindi ho un metodo programmato chiamare un metodo asincrono in quanto l'annotazione @Async permette esecuzioni simultanee:

@Service 
public class MyTask{ 
    @Async 
    public void doIt(){ 
     // this sometimes takes >1000ms, but the next execution is on time 
     ... 
    } 
} 

@Service 
public class MyTaskScheduler{ 
    ... 
    @Scheduled(fixedRate = 1000) 
    public void doIt(){ 
     myTask.doIt(); 
    } 
} 

@Configuration 
@EnableScheduling 
@EnableAsync 
public class MySpringJavaConfig{ 
    @Bean(destroyMethod = "shutdown") 
    public Executor taskScheduler() { 
     return Executors.newScheduledThreadPool(5); 
    } 
} 

dettagli noiosi della mia mondo reale scenario: nel mio codice di produzione ho un compito che richiede tra 10ms e 10 minuti a seconda del carico di lavoro corrente. idealmente, mi piacerebbe catturare un nuovo thread dal pool ogni 1000ms in modo che il numero di thread simultanei aumenti con il carico di lavoro. ovviamente ho un limite superiore sui thread in atto (tra gli altri controlli) per evitare che le cose sfuggano di mano.

risposta

3

Il TaskScheduler API (che sostiene alcuni comportamenti Scheduling Spring) sembra essere definite per evitare il problema si richiede

Schedule dato Runnable, invocando al momento dell'esecuzione specificato e successivamente con il periodo indicato.

Parametri

  • periodo l'intervallo tra le esecuzioni successive del task (in millisecondi)

successivamente e successiva sembrano indicare che l'esecuzione successiva si verificherà solo dopo che l'esecuzione corrente è stata completata.

Cosa c'è di più, ScheduledExecutorService#scheduleAtFixedRate(..) (che il built-in TaskScheduler implementazioni usare) dice anche

Se una qualsiasi esecuzione di questo compito richiede più tempo del suo periodo, quindi successive esecuzioni possono iniziare in ritardo, ma non lo farà contemporaneamente esegui.

Quindi c'è un altro livello dell'implementazione che impedisce il comportamento desiderato.

Una possibile soluzione e una che non consiglio poiché l'API non sembra essere costruita attorno ad essa, è quella di definire e fornire il proprio TaskScheduler che esegue l'attività contemporaneamente. Cerca su @EnableScheduling e SchedulingConfigurer per come registrare un TaskScheduler.

+0

apprezzo i riferimenti, e sto studiando la SchedulingConfigurer ed il modello di cron posti di lavoro basato. detto questo, non sono d'accordo con la tua interpretazione di "successivamente" e "successivi", e vorrei una soluzione (comunque scomoda) che realizzi il mio obiettivo. in quanto tale, ho intenzione di aspettare ancora un po 'prima di accettare questa risposta nella speranza che qualcuno possa elaborare una specifica configurazione di molla funzionale. –

1

la soluzione migliore che ho trovato finora è semplicemente utilizzare un delegato per rendere il metodo chiamate asincrone. questo è preferibile solo perché mi permette di dichiarare il programma nella stessa classe come il metodo che fa il lavoro:

@Service 
public class AsyncRunner { 
    @Async 
    public void run(Runnable runnable) { 
     runnable.run(); 
    } 
} 

@Service 
public class MyTask{ 
    ... 
    @Scheduled(fixedRate = 1000) 
    public void scheduleIt(){ 
     asyncRunner.run(this::doIt); 
    } 
    public void doIt(){ 
     // this sometimes takes >1000ms, but the next execution is on time 
     ... 
    } 
} 
Problemi correlati