¿Cómo puedo apagar los grupos ejecutor/programador de tareas de primavera antes de que se destruyan todos los demás frijoles en la aplicación web?


En una aplicación web de primavera tengo varios frijoles DAO y service layer. Una capa de servicio bean tiene métodos anotados @Async / @Scheduled. Estos métodos dependen de otros frijoles (autowired). He configurado dos grupos de subprocesos en 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"/>

Todo funciona como se espera. Mi problema es que no puedo conseguir un apagado limpio de los grupos de tareas para trabajar. Las tareas operan en la base de datos y en el sistema de archivos. Cuando paro la aplicación web tarda algún tiempo hasta que se detiene. Este indica que la propiedad waitForTasksToCompleteOnShutdown funciona. Sin embargo, obtengo IllegalStateExceptions en el registro que indica que algunos beans ya están destruidos, pero algunos hilos de tareas de worker todavía se están ejecutando y fallan porque sus dependencias están destruidas.

Hay un problema de JIRA que podría ser relevante: SPR-5387

Mi pregunta es: ¿Hay una manera de decirle a Spring que inicialice la tarea executor / scheduler beans last o hay una manera de decirle a Spring que los destruya ¿primero?

Mi entendimiento es que la destrucción tiene lugar en orden inverso. Por lo tanto el frijol init'ed último será destruido primero. Si los frijoles del grupo de subprocesos se destruyen primero, todas las tareas que se ejecutan actualmente terminarían y aún podrían acceder a los frijoles dependientes.

También he intentado usar el atributo depends-on en los grupos de subprocesos que se refieren a mi service bean que tiene las anotaciones @Async y @Scheduled. Parece que nunca son ejecutados entonces y yo no consigo errores de inicialización de contexto. Asumo que el servicio anotado bean de alguna manera necesita estos grupos de subprocesos inicializados primero y si uso depends-on invierto el orden y los hago no funcionales.

Author: tvirtualw, 2011-07-07

5 answers

De dos maneras:

  1. Tener un implemento bean ApplicationListener<ContextClosedEvent>. onApplicationEvent() será llamado antes del contexto y todos los frijoles serán destruidos.

  2. Tener un implemento de bean Lifecycleo SmartLifecycle. stop() será llamado antes del contexto y todos los frijoles serán destruidos.

De cualquier manera, puede apagar las cosas de la tarea antes de que se produzca el mecanismo de destrucción de frijoles.

Eg:

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

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

(Editar: Método fijo firma)

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-19 22:47:03

He añadido el siguiente código para terminar las tareas que puede utilizar. Puede cambiar los números de reintento.

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-12 10:26:43

Tuve problemas similares con los hilos que se iniciaron en Spring bean. Estos hilos no se cerraban correctamente después de que llamé albacea.shutdownNow () en el método @PreDestroy. Así que la solución para mí fue dejar que el hilo finsih con IO ya comenzó y comenzar no más IO, una vez que se llamó a @PreDestroy. Y aquí está el método @ PreDestroy. Para mi solicitud la espera de 1 segundo fue aceptable.

@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();
            }
        }
    }

Aquí he explicado todos los problemas que se enfrentan al tratar de cerrar 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-09 09:10:39

Si va a ser una aplicación basada en web, también puede usar la interfaz 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-05 09:46:45

Podemos agregar la propiedad" AwaitTerminationSeconds " para taskExecutor y TaskScheduler como se muestra a continuación,

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

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

La documentación de la propiedad" waitForTasksToCompleteOnShutdown " dice, cuando se llama a shutdown

"El cierre del contenedor de Spring continúa mientras se completan las tareas en curso. Si desea que este ejecutor bloquee y espere la finalización de las tareas antes de que el resto del contenedor continúe apagándose, por ejemplo , para mantener otros recursos que sus tareas puede necesitar -, establecer la propiedad "awaitTerminationSeconds" en lugar de o además de esta propiedad."

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

Por lo tanto, siempre se recomienda usar las propiedades waitForTasksToCompleteOnShutdown y awaitTerminationSeconds juntas. El valor de awaitTerminationSeconds depende de nuestra aplicación.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-18 21:22:46