Jak Mogę wyłączyć Pule wykonawców/harmonogramów zadań Spring, zanim wszystkie inne fasole w aplikacji internetowej zostaną zniszczone?

W wiosennej aplikacji internetowej mam kilka fasoli DAO i service layer. Jedna z warstw usług ma przypisane metody @Async / @Scheduled. Metody te zależą od innych (autowirowanych) ziaren. Skonfigurowałem dwie pule wątków w XML:

<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
     <property name="corePoolSize" value="2" />
     <property name="maxPoolSize" value="5" />
     <property name="queueCapacity" value="5" />
     <property name="waitForTasksToCompleteOnShutdown" value="true" />
     <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
        </property>
    </bean>

<bean id="taskScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
     <property name="poolSize" value="10" />
     <property name="waitForTasksToCompleteOnShutdown" value="true" />
     <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
        </property>
    </bean>

    <task:annotation-driven executor="taskExecutor" scheduler="taskScheduler"/>
Wszystko działa zgodnie z oczekiwaniami. Mój problem polega na tym, że nie mogę uzyskać czystego wyłączenia pul zadań do pracy. Zadania działają w bazie danych i w systemie plików. Kiedy Zatrzymuję aplikację webową, zajmuje to trochę czasu, zanim zostanie zatrzymana. To wskazuje, że właściwość waitForTasksToCompleteOnShutdown działa. Jednak dostaję IllegalStateExceptions w dzienniku wskazujące, że niektóre fasolki są już zniszczone, ale niektóre wątki zadań workera są nadal wykonywane i zawodzą, ponieważ ich zależności są zniszczone.

Istnieje problem JIRA, który może być istotny: SPR-5387

Moje pytanie brzmi: Czy Jest jakiś sposób żeby Spring zainicjował zadanie executor/scheduler na końcu czy jest jakiś sposób żeby Spring je zniszczył najpierw?

Rozumiem, że zniszczenie odbywa się w odwrotnej kolejności. W związku z tym fasola init'ed last zostanie zniszczona jako pierwsza. Jeśli wątek pool beans zostanie zniszczony jako pierwszy, wszystkie aktualnie wykonywane zadania zakończą się i nadal będą mogły uzyskać dostęp do zależnych beans.

Próbowałem również użyć atrybutu depends-on na pulach wątków odnoszących się do mojego serwisu bean, który ma adnotacje @Async i @Scheduled. Wydaje się, że nigdy nie są wykonywane wtedy i nie dostać błędy inicjalizacji kontekstu. Zakładam, że ten serwis w jakiś sposób potrzebuje najpierw tych pul wątków, a jak użyję to odwrócę kolejność i uczynię je niefunkcjonalnymi.

Author: tvirtualw, 2011-07-07

5 answers

Dwa sposoby:

  1. Mieć Bean zaimplement ApplicationListener<ContextClosedEvent>. onApplicationEvent() zostanie wywołana przed kontekstem i wszystkie ziarna zostaną zniszczone.

  2. Mieć implementację bean Lifecycle lub SmartLifecycle. stop() zostanie wywołana przed kontekstem i wszystkie ziarna zostaną zniszczone.

Tak czy siak możesz wyłączyć zadania, zanim zostanie uruchomiony mechanizm niszczenia fasoli.

Eg:

@Component
public class ContextClosedHandler implements ApplicationListener<ContextClosedEvent> {
    @Autowired ThreadPoolTaskExecutor executor;
    @Autowired ThreadPoolTaskScheduler scheduler;

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        scheduler.shutdown();
        executor.shutdown();
    }       
}

(Edit: Fixed method podpis)

 48
Author: sourcedelica,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-19 22:47:03

Dodałem poniżej kod, aby zakończyć zadania Można go używać. Możesz zmienić numery prób.

package com.xxx.test.schedulers;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;

import com.xxx.core.XProvLogger;

@Component
class ContextClosedHandler implements ApplicationListener<ContextClosedEvent> , ApplicationContextAware,BeanPostProcessor{


private ApplicationContext context;

public Logger logger = XProvLogger.getInstance().x;

public void onApplicationEvent(ContextClosedEvent event) {


    Map<String, ThreadPoolTaskScheduler> schedulers = context.getBeansOfType(ThreadPoolTaskScheduler.class);

    for (ThreadPoolTaskScheduler scheduler : schedulers.values()) {         
        scheduler.getScheduledExecutor().shutdown();
        try {
            scheduler.getScheduledExecutor().awaitTermination(20000, TimeUnit.MILLISECONDS);
            if(scheduler.getScheduledExecutor().isTerminated() || scheduler.getScheduledExecutor().isShutdown())
                logger.info("Scheduler "+scheduler.getThreadNamePrefix() + " has stoped");
            else{
                logger.info("Scheduler "+scheduler.getThreadNamePrefix() + " has not stoped normally and will be shut down immediately");
                scheduler.getScheduledExecutor().shutdownNow();
                logger.info("Scheduler "+scheduler.getThreadNamePrefix() + " has shut down immediately");
            }
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    Map<String, ThreadPoolTaskExecutor> executers = context.getBeansOfType(ThreadPoolTaskExecutor.class);

    for (ThreadPoolTaskExecutor executor: executers.values()) {
        int retryCount = 0;
        while(executor.getActiveCount()>0 && ++retryCount<51){
            try {
                logger.info("Executer "+executor.getThreadNamePrefix()+" is still working with active " + executor.getActiveCount()+" work. Retry count is "+retryCount);
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if(!(retryCount<51))
            logger.info("Executer "+executor.getThreadNamePrefix()+" is still working.Since Retry count exceeded max value "+retryCount+", will be killed immediately");
        executor.shutdown();
        logger.info("Executer "+executor.getThreadNamePrefix()+" with active " + executor.getActiveCount()+" work has killed");
    }
}


@Override
public void setApplicationContext(ApplicationContext context)
        throws BeansException {
    this.context = context;

}


@Override
public Object postProcessAfterInitialization(Object object, String arg1)
        throws BeansException {
    return object;
}


@Override
public Object postProcessBeforeInitialization(Object object, String arg1)
        throws BeansException {
    if(object instanceof ThreadPoolTaskScheduler)
        ((ThreadPoolTaskScheduler)object).setWaitForTasksToCompleteOnShutdown(true);
    if(object instanceof ThreadPoolTaskExecutor)
        ((ThreadPoolTaskExecutor)object).setWaitForTasksToCompleteOnShutdown(true);
    return object;
}

}

 7
Author: fatih tekin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-12 10:26:43

Miałem podobne problemy z wątkami uruchamianymi w fasoli wiosennej. Te wątki nie zamykały się poprawnie po tym, jak zadzwoniłem do executora.shutdownNow () w metodzie @PreDestroy. Więc rozwiązaniem dla mnie było niech wątek finsih z IO już się rozpoczął i start no more IO, raz @ PreDestroy został wywołany. A oto metoda @PreDestroy. Dla mojej aplikacji oczekiwanie na 1 sekundę było dopuszczalne.

@PreDestroy
    public void beandestroy() {
        this.stopThread = true;
        if(executorService != null){
            try {
                // wait 1 second for closing all threads
                executorService.awaitTermination(1, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

Tutaj wyjaśniłem wszystkie problemy napotykane podczas próby zamknięcia wątki. http://programtalk.com/java/executorservice-not-shutting-down/

 3
Author: awsome,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-09 09:10:39

Jeśli ma to być aplikacja internetowa, Możesz również użyć interfejsu ServletContextListener.

public class SLF4JBridgeListener implements ServletContextListener {

   @Autowired 
   ThreadPoolTaskExecutor executor;

   @Autowired 
   ThreadPoolTaskScheduler scheduler;

    @Override
    public void contextInitialized(ServletContextEvent sce) {

    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
         scheduler.shutdown();
         executor.shutdown();     

    }

}

 1
Author: Balasubramanian Jayaraman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-05 09:46:45

Możemy dodać właściwość "Waitterminationseconds" zarówno dla taskExecutor, jak i taskScheduler, jak poniżej,

<property name="awaitTerminationSeconds" value="${taskExecutor .awaitTerminationSeconds}" />

<property name="awaitTerminationSeconds" value="${taskScheduler .awaitTerminationSeconds}" />

Dokumentacja dla właściwości "waitForTasksToCompleteOnShutdown" mówi, że po wywołaniu shutdown

"zamykanie kontenerów Springa jest kontynuowane podczas wykonywania bieżących zadań. Jeśli chcesz, aby ten executor blokował i czekał na zakończenie zadań, zanim reszta kontenera nadal się wyłączy - np. w celu utrzymania innych zasobów, które twoje zadania może wymagać -, ustawić właściwość "waitterminationseconds" zamiast lub jako dodatek do tej właściwości."

Https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.html#setWaitForTasksToCompleteOnShutdown-boolean-

Dlatego zawsze zaleca się używanie właściwości waitForTasksToCompleteOnShutdown i waitterminationseconds razem. Wartość oczekiwanych sekund zależy od naszej aplikacji.

 0
Author: Manjunath D R,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-18 21:22:46