Läuft die mit Annotationen versehene Methode von spring @Scheduled auf verschiedenen Threads?

78

Ich habe mehrere Methoden mit Anmerkungen versehen @Scheduled(fixedDelay=10000).

Im Anwendungskontext habe ich dieses annotationsgesteuerte Setup:

<task:annotation-driven />

Das Problem ist, dass einige der Methodenausführungen manchmal um Sekunden und sogar Minuten verzögert werden.

Ich gehe davon aus, dass selbst wenn eine Methode eine Weile braucht, um die Ausführung abzuschließen, die anderen Methoden immer noch ausgeführt werden. Ich verstehe die Verzögerung also nicht.

Gibt es eine Möglichkeit, die Verzögerung zu verringern oder sogar zu beseitigen?

froi
quelle

Antworten:

58

Der Vollständigkeit halber zeigt der folgende Code die einfachste Möglichkeit, den Scheduler mit Java Config zu konfigurieren:

@Configuration
@EnableScheduling
public class SpringConfiguration {

    @Bean(destroyMethod = "shutdown")
    public Executor taskScheduler() {
        return Executors.newScheduledThreadPool(5);
    }
    ...

Wenn mehr Kontrolle gewünscht wird, kann eine @ConfigurationKlasse implementieren SchedulingConfigurer.

G. Demecki
quelle
4
Da die ExecutorSchnittstelle keine shutdown()Methode hat, ist es wahrscheinlich besser, sie ExecutorServiceals Rückgabetyp zu verwenden, um die Bean-Definition korrekt zu machen. Oder wird Spring zur Laufzeit den tatsächlichen Bean-Typ ermitteln?
Vladimir Vagaytsev
1
XML config -<task:scheduler id="taskScheduler" pool-size="5"/>
whoami
1
Sehen Sie hier für ein Beispiel eines SchedulingConfigurer
crusy
1
Funktioniert nicht mit Spring autoconfig zusammen: Die in der Klassenpfadressource [org / springframework / boot / autoconfigure / task / TaskSchedulingAutoConfiguration.class] definierte Bean 'taskScheduler' konnte nicht registriert werden. Eine Bean mit diesem Namen wurde bereits in der Klassenpfadressource definiert ...
Phil
52

In der Dokumentation zur Zeitplanung heißt es:

Wenn Sie kein Attribut für die Poolgröße angeben, verfügt der Standard-Thread-Pool nur über einen einzelnen Thread.

Wenn Sie also viele geplante Aufgaben haben, sollten Sie den Scheduler wie in der Dokumentation erläutert so konfigurieren, dass er einen Pool mit mehr Threads hat, um sicherzustellen, dass eine lange Aufgabe nicht alle anderen verzögert.

JB Nizet
quelle
20
Nur zur Information: Es hilft nicht, nur einen Link zu den allgemeinen Dokumenten bereitzustellen und zu sagen, dass Sie es konfiguriert haben ... Ein Beispiel anzugeben ist unendlich hilfreicher. Ich habe für g gestimmt. Demeckis Antwort unten und nicht deine aus genau diesem Grund ... Nur Tipps, um
weiterzukommen
Die zitierte Dokumentation bezieht sich auf task:schedulereinen pool-sizeParameter. Gilt dies für die @ScheduledAnnotation, die keine poolbezogenen Parameter enthält?
David Soroko
@ DavidSoroko Ich glaube schon.
vom
34

Eine mit Annotationen versehene Methode @Scheduledsoll zu einem bestimmten Zeitpunkt separat auf einem anderen Thread ausgeführt werden.

Wenn Sie TaskSchedulerin Ihrer Konfiguration keine angegeben haben , wird Spring verwendet

Executors.newSingleThreadScheduledExecutor();

Dies gibt einen zurück ScheduledExecutorService, der auf einem einzelnen Thread ausgeführt wird. Wenn Sie über mehrere @ScheduledMethoden verfügen , müssen diese, obwohl sie geplant sind, jeweils warten, bis der Thread die Ausführung der vorherigen Aufgabe abgeschlossen hat. Es kann immer wieder zu Verzögerungen kommen, da sich die Warteschlange schneller füllt als sie sich leert.

Stellen Sie sicher, dass Sie Ihre Planungsumgebung mit einer geeigneten Anzahl von Threads konfigurieren.

Sotirios Delimanolis
quelle
Link zur Spring-Dokumentation?
David Soroko
@DavidSoroko Dies ist in Javadoc nicht sofort offensichtlich. Es ist einfacher, es im Quellcode zu sehen. @Scheduled(und @EnableScheduling) wird durch Registrierung von a behandelt ScheduledAnnotationBeanPostProcessor. Dieser Postprozessor verwendet einen, ScheduledTaskRegistrar der standardmäßig diesen Single-Thread verwendetScheduledExecutorService .
Sotirios Delimanolis
4
Ich denke, Sie sind zu nett - es ist überhaupt nicht in der Dokumentation. Der Quellcode kann sich von Release zu Release ändern.
David Soroko
19

UPDATE 2019

Es gibt auch eine Eigenschaft, die Sie in Ihrer Anwendungseigenschaftendatei festlegen können, um die Poolgröße zu erhöhen:

spring.task.scheduling.pool.size=10

Scheint seit Spring Boot 2.1.0 da zu sein.

L.Butz
quelle
2
Dies ist die einzige Möglichkeit, wenn die Spring Boot-Version> = 2.0 ist. Override taskScheduler () ist nutzlos
Jean
10

Sie können verwenden:

@Bean()
public  ThreadPoolTaskScheduler  taskScheduler(){
    ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
    taskScheduler.setPoolSize(2);
    return  taskScheduler;
}
sj8515465
quelle
2
Ich denke, es ist eine gute Praxis, anzurufen, taskScheduler.initialize();bevor Sie Ihre Instanz vontaskScheduler
Orby
6

Die Annotation @EnableScheduling enthält die wichtigsten Informationen und deren Lösung:

Standardmäßig wird nach einer zugeordneten Scheduler-Definition gesucht: entweder nach einer eindeutigen TaskScheduler-Bean im Kontext oder nach einer TaskScheduler-Bean mit dem Namen "taskScheduler". Die gleiche Suche wird auch für eine ScheduledExecutorService-Bean durchgeführt. Wenn keines der beiden Probleme gelöst werden kann, wird ein lokaler Single-Threaded-Standardplaner erstellt und im Registrar verwendet .

Wenn mehr Kontrolle gewünscht wird, kann eine @ Configuration-Klasse SchedulingConfigurer implementieren. Dies ermöglicht den Zugriff auf die zugrunde liegende ScheduledTaskRegistrar-Instanz. Das folgende Beispiel zeigt beispielsweise, wie Sie den Executor anpassen, der zum Ausführen geplanter Aufgaben verwendet wird:

 @Configuration
 @EnableScheduling
 public class AppConfig implements SchedulingConfigurer {

     @Override
     public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
         taskRegistrar.setScheduler(taskExecutor());
     }

     @Bean(destroyMethod="shutdown")
     public Executor taskExecutor() {
         return Executors.newScheduledThreadPool(100);
     }
 }

(Betonung hinzugefügt)

jgreen
quelle
0

Fügen Sie mithilfe der XML-Datei die folgenden Zeilen hinzu.

<task:scheduler id="taskScheduler" pool-size="15" />
<task:scheduled-tasks scheduler="taskScheduler" >
....
</task:scheduled-tasks>
Ashok Parmar
quelle
-1

Standardfeder mit einem einzelnen Thread für die Zeitplanaufgabe. Sie können @Configuration für Klassenimplementierungen verwenden, die SchedulingConfigurer implementieren. Referenz:https://crmepham.github.io/spring-boot-multi-thread-scheduling/

zhaoyou
quelle