Springboot如何優(yōu)雅的關(guān)閉應(yīng)用
使用Spring Boot Actuator會中斷運行中的業(yè)務(wù)嗎?
當你向 /actuator/shutdown 端點發(fā)送 POST 請求以關(guān)閉應(yīng)用時,Spring Boot Actuator 會觸發(fā)應(yīng)用的關(guān)閉操作。這意味著應(yīng)用會執(zhí)行相應(yīng)的關(guān)閉邏輯,并嘗試優(yōu)雅地停止正在運行的業(yè)務(wù)。
如果你的業(yè)務(wù)邏輯中實現(xiàn)了優(yōu)雅關(guān)閉的機制,例如捕獲了中斷信號并正確處理了中斷,那么應(yīng)用關(guān)閉時不會突然中斷運行中的業(yè)務(wù)。相反,應(yīng)用會嘗試完成當前正在執(zhí)行的任務(wù),然后安全地關(guān)閉。這種方式可以確保在關(guān)閉應(yīng)用時不會丟失數(shù)據(jù)或者導致不一致的狀態(tài)。
然而,如果你的業(yè)務(wù)邏輯沒有實現(xiàn)優(yōu)雅關(guān)閉的機制,或者在關(guān)閉操作中遇到了異常,那么關(guān)閉應(yīng)用時可能會導致正在運行的業(yè)務(wù)被中斷。這取決于應(yīng)用的具體實現(xiàn)和業(yè)務(wù)邏輯。
如何使用Spring Boot Actuator關(guān)閉應(yīng)用
1.引入Actuator包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency2.application.yml配置
# 開發(fā)環(huán)境配置
server:
# 服務(wù)器的HTTP端口,默認為80
port: 80
management:
endpoint:
shutdown:
enabled: true
endpoints:
web:
exposure:
include: "shutdown"
base-path: /monitor3.CURL 命令關(guān)閉應(yīng)用的示例
curl -X POST http://localhost:80/monitor/shutdown
4.運行截圖

使用Spring Boot Actuator關(guān)閉應(yīng)用,會終止正在運行中的程序,如果想要運行中的業(yè)務(wù)不中斷,需要自定義關(guān)閉器的關(guān)閉事件,使運行中的程序處理完成才關(guān)閉應(yīng)用。
如何自定義關(guān)閉監(jiān)聽器關(guān)閉事件優(yōu)雅的關(guān)閉應(yīng)用
本實例使用異步線程任務(wù)來模擬運行中的任務(wù)。
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.*;
/**
* 異步任務(wù)工具類
*/
@Slf4j
public class TaskExecutorUtil {
// Task運行狀態(tài)
public static volatile boolean isRunning = true;
// Task運行結(jié)果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執(zhí)行
*/
public static void executeTask(CommonTask task) {
Future<?> future = executorPool.submit(task);
runningTasks.add(future);
}
/**
* 關(guān)閉運行中所有線程,如果任務(wù)正在執(zhí)行中,待執(zhí)行完成再中斷該線程
*/
public static void stopAllTasks() {
isRunning = false;
for (Future<?> future : runningTasks) {
try {
boolean flag = true;
while (flag) {
// 如果任務(wù)還在執(zhí)行中,就休眠100ms
if(!future.isDone()) {
Thread.sleep(100);
} else {
flag = false;
}
}
} catch (InterruptedException e) {
// 打印異常
log.error("TaskExecutorUtil stopAllTasks {}", e.getMessage(), e);
} finally {
// 中斷任務(wù)
future.cancel(true);
}
}
}
/**
* 關(guān)閉線程池
*/
public static void waitForTasksToComplete() {
// 等待所有任務(wù)完成
executorPool.shutdown();
}
/**
* 核心線程數(shù)
*/
private static int corePoolSize() {
return Runtime.getRuntime().availableProcessors();
}
/**
* 最大線程數(shù)
*/
private static int maximumPoolSize() {
return Runtime.getRuntime().availableProcessors() * 2;
}
}2.定義監(jiān)聽 Spring 應(yīng)用的關(guān)閉事件
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;
/**
* 監(jiān)聽 Spring 應(yīng)用的關(guān)閉事件
*/
@Slf4j
@Component
public class ShutdownListener implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
log.info("應(yīng)用正在關(guān)閉...");
// 中斷所有正在運行的任務(wù)
TaskExecutorUtil.stopAllTasks();
// 等待所有任務(wù)完成
TaskExecutorUtil.waitForTasksToComplete();
log.info("應(yīng)用已經(jīng)關(guān)閉...");
}
}3.定義異步任務(wù)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 {
// 創(chuàng)建并執(zhí)行任務(wù)
MyTask task = new MyTask();
TaskExecutorUtil.executeTask(task);
System.out.println("任務(wù)已啟動...");
}
}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("本次任務(wù)開始執(zhí)行.......");
try {
for(int i = 0; i < 10; i++) {
// 模擬任務(wù)執(zhí)行時間,等待1s
Thread.sleep(1000);
log.info("i = {}", i);
}
log.info("本次任務(wù)執(zhí)行完成.......");
} catch (InterruptedException e) {
// 處理中斷請求
Thread.currentThread().interrupt();
log.info("任務(wù)被中斷.......");
}
}
}
}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運行中時執(zhí)行了關(guān)閉任務(wù)操作,但是待Task業(yè)務(wù)執(zhí)行完成了才關(guān)閉了應(yīng)用。
總結(jié)
本文介紹了如何通過使用Spring Boot Actuator關(guān)閉應(yīng)用,并自定義關(guān)閉監(jiān)聽器關(guān)閉事件,優(yōu)雅的關(guān)閉應(yīng)用,給出了如何優(yōu)雅的關(guān)閉異步任務(wù)的實例,有興趣的小伙伴可以參考。
相關(guān)文章
mybatis相同的sql查詢第二次查不出結(jié)果問題
這篇文章主要介紹了mybatis相同的sql查詢第二次查不出結(jié)果問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
詳解Springboot Oauth2 Server搭建Oauth2認證服務(wù)
java中mybatis和hibernate的用法總結(jié)
java組件SmartUpload和FileUpload實現(xiàn)文件上傳功能
SpringBoot整合Kafka生產(chǎn)環(huán)境標準配置

