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ù)或者導(dǎo)致不一致的狀態(tài)。
然而,如果你的業(yè)務(wù)邏輯沒有實現(xiàn)優(yōu)雅關(guān)閉的機制,或者在關(guān)閉操作中遇到了異常,那么關(guān)閉應(yīng)用時可能會導(dǎo)致正在運行的業(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> </dependency
2.application.yml配置
# 開發(fā)環(huán)境配置 server: # 服務(wù)器的HTTP端口,默認為80 port: 80 management: endpoint: shutdown: enabled: true endpoints: web: exposure: include: "shutdown" base-path: /monitor
3.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)文章
使用Springboot+poi上傳并處理百萬級數(shù)據(jù)EXCEL
這篇文章主要介紹了使用Springboot+poi上傳并處理百萬級數(shù)據(jù)EXCEL,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12MybatisPlus自定義Sql實現(xiàn)多表查詢的示例
這篇文章主要介紹了MybatisPlus自定義Sql實現(xiàn)多表查詢的示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08spring boot高并發(fā)下耗時操作的實現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于spring boot高并發(fā)下耗時操作的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11