如何友好地关闭Spring启动线程池项目,该项目是24x7运行



我已经创建了Spring Boot线程池项目,该项目的线程一旦产生就需要运行24x7,但是当我需要在服务器中停止应用程序以进行某种维护时,它应该在完成当前任务后关闭并且不承担任何新任务。

我的代码是:config class

@Configuration
public class ThreadConfig {
    @Bean
    public ThreadPoolTaskExecutor taskExecutor(){
    ThreadPoolTaskExecutor executorPool = new ThreadPoolTaskExecutor();
    executorPool.setCorePoolSize(10);
    executorPool.setMaxPoolSize(20);
    executorPool.setQueueCapacity(10);
    executorPool.setWaitForTasksToCompleteOnShutdown(true);
    executorPool.setAwaitTerminationSeconds(60);
    executorPool.initialize();
    return executorPool;
}
}

可运行的类

@Component
@Scope("prototype")
public class DataMigration implements Runnable {
String name;
private boolean run=true;
public DataMigration(String name) {
    this.name = name;
}
@Override
public void run() {
    while(run){
    System.out.println(Thread.currentThread().getName()+" Start Thread = "+name);
    processCommand();
    System.out.println(Thread.currentThread().getName()+" End Thread = "+name);
    if(Thread.currentThread().isInterrupted()){
        System.out.println("Thread Is Interrupted");
        break;
    }
    }
}
private void processCommand() {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
public void shutdown(){
    this.run = false;
}
}

主类:

@SpringBootApplication
public class DataMigrationPocApplication implements CommandLineRunner{
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
public static void main(String[] args) {
    SpringApplication.run(DataMigrationPocApplication.class, args);
}
@Override
public void run(String... arg0) throws Exception {
    for(int i = 1; i<=20 ; i++){
        taskExecutor.execute(new DataMigration("Task " + i));
    }
    for (;;) {
        int count = taskExecutor.getActiveCount();
        System.out.println("Active Threads : " + count);
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (count == 0) {
            taskExecutor.shutdown();
            break;
        }
    }
    System.out.println("Finished all threads");
}
}

我需要帮助来了解我是否需要停止春季启动应用程序,它应该停止运行的所有20个线程(24x7),否则在循环和退出时完成当前循环后。

我会在此代码中提出几个更改以解决问题

1)由于在您的POC ProcessCommand呼叫thread.sleep中,当您关闭执行程序时,它会中断工人中断的呼叫,但在您的代码中几乎被忽略。之后,有 if(thread.currentthread()。is Interrupted())检查该检查将返回false,以上述原因。下面的帖子概述了类似的问题

thread.interrupt()如何设置标志?

以下代码更改应解决问题:

private void processCommand() {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
        shutdown();
    }
}

2)还因为threadConfig :: Taskexecutor executorpool.setWaitfortaskStocompleteonShutdown(true)Spring将调用executor.shutdown而不是exector.shutdownnow。根据javadoc executorService.shutdown

启动有序的关闭,以前提交的任务是 执行,但不会接受新任务。

所以我建议设置

executorPool.setWaitForTasksToCompleteOnShutdown(false);

在此代码中需要改进的其他事情:尽管数据登录作为组成部分,但该类的实例并非在春季之前将其编入。您应该尝试使用类似于ThreadConfig :: Taskexecutor类似的工厂方法,以使春季启动Datamigrations例如,将其他Bean注入Datamigration实例。

为了关闭执行程序,当在Linux环境上运行JAR文件您可以添加执行器模块并启用关闭端点:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

在application.properties:

endpoints.shutdown.enabled=true

它将启用JMX关闭端点,您可以在其上调用关闭。如果您希望完成任务的当前工作周期,则应设置

executorPool.setWaitForTasksToCompleteOnShutdown(true);

为了在Linux Env 上连接到JVM进程>远程指定RMI注册表端口。这是一篇详细的文章:如何访问Spring-boot JMX远程

如果您只需要从本地env连接到JMX,则可以运行JSONCOLE或命令行工具:从Shell脚本调用JMX MBEAN方法

这是使用以下工具之一的示例uf -jmxterm

$>run -d org.springframework.boot: -b        org.springframework.boot:name=shutdownEndpoint,type=Endpoint shutdown
#calling operation shutdown of mbean  org.springframework.boot:name=shutdownEndpoint,type=Endpoint with params  []
#operation returns:
{
  message = Shutting down, bye...;
}

最新更新