java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Springboot关闭应用

Springboot如何优雅的关闭应用

作者:一棵星

这篇文章主要介绍了Springboot如何优雅的关闭应用问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

使用Spring Boot Actuator会中断运行中的业务吗?

当你向 /actuator/shutdown 端点发送 POST 请求以关闭应用时,Spring Boot Actuator 会触发应用的关闭操作。这意味着应用会执行相应的关闭逻辑,并尝试优雅地停止正在运行的业务。

如果你的业务逻辑中实现了优雅关闭的机制,例如捕获了中断信号并正确处理了中断,那么应用关闭时不会突然中断运行中的业务。相反,应用会尝试完成当前正在执行的任务,然后安全地关闭。这种方式可以确保在关闭应用时不会丢失数据或者导致不一致的状态。

然而,如果你的业务逻辑没有实现优雅关闭的机制,或者在关闭操作中遇到了异常,那么关闭应用时可能会导致正在运行的业务被中断。这取决于应用的具体实现和业务逻辑。

如何使用Spring Boot Actuator关闭应用

1.引入Actuator包

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

2.application.yml配置

# 开发环境配置
server:
  # 服务器的HTTP端口,默认为80
  port: 80

management:
  endpoint:
    shutdown:
      enabled: true
  endpoints:
    web:
      exposure:
        include: "shutdown"
      base-path: /monitor

3.CURL 命令关闭应用的示例

curl -X POST http://localhost:80/monitor/shutdown

4.运行截图

使用Spring Boot Actuator关闭应用,会终止正在运行中的程序,如果想要运行中的业务不中断,需要自定义关闭器的关闭事件,使运行中的程序处理完成才关闭应用。

如何自定义关闭监听器关闭事件优雅的关闭应用

本实例使用异步线程任务来模拟运行中的任务。

1.定义异步管理类

package com.angel.ocean.tool.util;

import com.angel.ocean.tool.task.CommonTask;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * 异步任务工具类
 */
@Slf4j
public class TaskExecutorUtil {

    // Task运行状态
    public static volatile boolean isRunning = true;

    // Task运行结果Future集合
    private final static List<Future<?>> runningTasks = new ArrayList<>();

    // 线程池
    static ExecutorService executorPool = new ThreadPoolExecutor(corePoolSize(), maximumPoolSize(), 10, TimeUnit.MINUTES,
            new ArrayBlockingQueue<Runnable>(1000), new ThreadPoolExecutor.CallerRunsPolicy());

    /**
     * Task执行
     */
    public static void executeTask(CommonTask task) {
        Future<?> future = executorPool.submit(task);
        runningTasks.add(future);
    }

    /**
     * 关闭运行中所有线程,如果任务正在执行中,待执行完成再中断该线程
     */
    public static void stopAllTasks() {
        isRunning = false;
        for (Future<?> future : runningTasks) {
            try {
                boolean flag = true;
                while (flag) {
                    // 如果任务还在执行中,就休眠100ms
                    if(!future.isDone()) {
                        Thread.sleep(100);
                    } else {
                        flag = false;
                    }
                }
            } catch (InterruptedException e) {
                // 打印异常
                log.error("TaskExecutorUtil stopAllTasks {}", e.getMessage(), e);
            } finally {
                // 中断任务
                future.cancel(true);
            }
        }
    }

    /**
     * 关闭线程池
     */
    public static void waitForTasksToComplete() {
        // 等待所有任务完成
        executorPool.shutdown();
    }

    /**
     * 核心线程数
     */
    private static int corePoolSize() {
        return Runtime.getRuntime().availableProcessors();
    }

    /**
     * 最大线程数
     */
    private static int maximumPoolSize() {
        return Runtime.getRuntime().availableProcessors() * 2;
    }
}

2.定义监听 Spring 应用的关闭事件

package com.angel.ocean.tool.event;

import com.angel.ocean.tool.util.TaskExecutorUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

/**
 *  监听 Spring 应用的关闭事件
 */
@Slf4j
@Component
public class ShutdownListener implements ApplicationListener<ContextClosedEvent> {
    @Override
    public void onApplicationEvent(ContextClosedEvent event) {

        log.info("应用正在关闭...");

        // 中断所有正在运行的任务
        TaskExecutorUtil.stopAllTasks();

        // 等待所有任务完成
        TaskExecutorUtil.waitForTasksToComplete();

        log.info("应用已经关闭...");
    }
}

3.定义异步任务Task

package com.angel.ocean.tool.runner;

import com.angel.ocean.tool.task.MyTask;
import com.angel.ocean.tool.util.TaskExecutorUtil;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class MyTaskRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 创建并执行任务
        MyTask task = new MyTask();
        TaskExecutorUtil.executeTask(task);
        System.out.println("任务已启动...");
    }
}
package com.angel.ocean.tool.task;

import com.angel.ocean.tool.util.TaskExecutorUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MyTask extends CommonTask {
    @Override
    public void handler() {
        while (TaskExecutorUtil.isRunning) {
            log.info("本次任务开始执行.......");
            try {
                for(int i = 0; i < 10; i++) {
                    // 模拟任务执行时间,等待1s
                    Thread.sleep(1000);
                    log.info("i = {}", i);
                }
                log.info("本次任务执行完成.......");
            } catch (InterruptedException e) {
                // 处理中断请求
                Thread.currentThread().interrupt();
                log.info("任务被中断.......");
            }
        }
    }
}
package com.angel.ocean.tool.task;

import lombok.extern.slf4j.Slf4j;

/**
 * Task抽象类
 */
@Slf4j
public abstract class CommonTask implements Runnable {

    @Override
    public void run() {
        handler();
    }

    public void handler() {

    }
}

4.验证截图

可以看出,在Task运行中时执行了关闭任务操作,但是待Task业务执行完成了才关闭了应用。

总结

本文介绍了如何通过使用Spring Boot Actuator关闭应用,并自定义关闭监听器关闭事件,优雅的关闭应用,给出了如何优雅的关闭异步任务的实例,有兴趣的小伙伴可以参考。

您可能感兴趣的文章:
阅读全文