Springboot如何优雅的关闭应用

 更新时间:2024年08月19日 09:54:55   作者:一棵星  
这篇文章主要介绍了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关闭应用,并自定义关闭监听器关闭事件,优雅的关闭应用,给出了如何优雅的关闭异步任务的实例,有兴趣的小伙伴可以参考。

相关文章

  • idea git未提交代码文件名字变色(图解)

    idea git未提交代码文件名字变色(图解)

    这篇文章主要介绍了idea git未提交代码文件名字变色,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • mybatis相同的sql查询第二次查不出结果问题

    mybatis相同的sql查询第二次查不出结果问题

    这篇文章主要介绍了mybatis相同的sql查询第二次查不出结果问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • Java如何通过jstack命令查询日志

    Java如何通过jstack命令查询日志

    在分析线上问题时常使用到jstack <PID>命令将当时Java应用程序的线程堆栈dump出来,面对jstack 日志,我们如何查看?下面小编给大家介绍下Java如何通过jstack命令查询日志,感兴趣的朋友一起看看吧
    2023-03-03
  • 详解Springboot Oauth2 Server搭建Oauth2认证服务

    详解Springboot Oauth2 Server搭建Oauth2认证服务

    这篇文章主要介绍了Springboot Oauth2 Server 搭建Oauth2认证服务,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-05-05
  • java中mybatis和hibernate的用法总结

    java中mybatis和hibernate的用法总结

    在本篇文章里小编给大家整理的是一篇关于java中mybatis和hibernate的用法总结内容,有兴趣的朋友们可以学习参考下。
    2021-01-01
  • Spring AOP原理解析

    Spring AOP原理解析

    AOP面向切片编程思想,是一种对一类问题集中处理的思想,Spring AOP就是为了解决这些问题存在,是AOP思想的其中一种实现方式,下面给大家详细介绍Spring AOP原理解析,感兴趣的朋友跟随小编一起看看吧
    2026-02-02
  • SpringBoot的@Conditional条件注解详解

    SpringBoot的@Conditional条件注解详解

    这篇文章主要介绍了SpringBoot的@Conditional条件注解详解,打开每个自动配置类,都会看到@Conditional或其衍生的条件注解,本节我们来认识下@Conditional注解,需要的朋友可以参考下
    2023-12-12
  • java组件SmartUpload和FileUpload实现文件上传功能

    java组件SmartUpload和FileUpload实现文件上传功能

    这篇文章主要为大家详细介绍了java组件SmartUpload和FileUpload实现文件上传功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • Java并发编程之工具类Semaphore的使用

    Java并发编程之工具类Semaphore的使用

    目前几乎所有的语言都支持信号量机制,Java也不例外.Java中提供了Semaphore并发工具类来支持信号量机制,下面我们就来了解Java实现的信号量机制,文中有非常详细的介绍,需要的朋友可以参考下
    2021-06-06
  • SpringBoot整合Kafka生产环境标准配置

    SpringBoot整合Kafka生产环境标准配置

    本文介绍了在SpringBoot 3.x项目中集成Apache Kafka的完整方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-04-04

最新评论