SpringBoot 優雅停止服務的幾種方法 - 第309篇

 

相關歷史文章(閱讀本文之前,您可能需要先看下之前的系列👇

國內最全的Spring Boot系列之三

一分鐘get:緩存穿透、緩存擊穿、緩存雪崩 - 第304篇

布隆過濾器Bloom Filter竟然讓我解決了一個大廠的問題 - 第305篇

100G的文件如何讀取 - 第306篇

100G的文件如何讀取續集 - 第307篇

Java語言的優雅停機 - 第308篇

 

師傅:對於java的shutdown hook有所瞭解之後,我們還是重點來看看Spring Boot如何優化停止服務吧。

悟纖:師傅,這節課那我認真聽了,不然到時候生產環境都不知道如何正確停止服務了。

師傅:你這話… 說的… 師傅講的每節課都很重要,好不。

悟纖:師傅,每節課我都有認真聽的,不行待會你隨便考我下。

師傅:那我們今天先把這個SpringBoot如何優雅停止學習完,在慢慢折磨你…

悟纖:師傅,我腦瓜怎麼就嗡嗡的吶。

 

 

一、準備工作

1.1 準備一個項目

       隨便創建一個Spring Boot項目,這裏使用的是截止到2020年5月 最新的版本:2.2.7.RELEASE。

 

1.2 創建一個bean

       我們創建一個Bean ,有一個銷燬的方法:

@Component
public class TerminateBean {

    @PreDestroy
    public void preDestroy() {
        System.out.println("TerminalBean is destroyed");
    }

}

       在接下來的測試中,主要是看這個preDestroy會不會被執行。

 

1.3 添加actuator的功能

       SpringBoot 提供的actuator的功能,它可以執行shutdown, health, info等,默認情況下,actuator的shutdown是disable的,我們需要打開它。

       利用actuator也是停止服務的方式,在下面會使用到,這裏先把依賴添加進來。

引入acturator的maven依賴:

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

打開shutdown節點:

       修改applicatoin.properties文件:

management.endpoint.shutdown.enabled=true
management.endpoints.web.exposure.include=*

 

       這裏暴露了所有的,也可以指定暴露shutdown:

management.endpoint.shutdown.enabled=true
management.endpoints.web.exposure.include=shutdown

 

二、SpringBoot的n中停機操作

2.1 Run As – Java Application運行 – Console Termiante關閉

       我們使用Run As – Java Application的方式進行運行我們的Application,啓動成功之後,使用Console中的Terminate關閉。

此時控制檯不會執行preDestroy方法。

 

2.2 Run As – Spring Boot App運行 – Console Terminate關閉

我們使用Run As – Spring Boot App的方式進行運行我們的Application,啓動成功之後,使用Console中的Terminate關閉。

       此時控制檯會執行preDestroy方法。

 

2.3 Run As – Java Application運行 – kill -15 pid關閉

       我們使用Run As – Java Application的方式進行運行我們的Application,啓動成功之後,kill -15 pid。

       此時控制檯會執行preDestroy方法。

2020-05-09 19:47:08.640  INFO 682 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
TerminalBean is destroyed

 

       注意控制檯的打印信息:有一個我們在前面講過的非常熟悉的extShutdownHook,不然猜出Spring底層也是使用了

Runtime.getRuntime().addShutdownHook(Thread hook);

 

       我們看下源碼:

org.springframework.context.support.AbstractApplicationContext.registerShutdownHook()

@Override
	public void registerShutdownHook() {
		if (this.shutdownHook == null) {
			// No shutdown hook registered yet.
			this.shutdownHook = new Thread() {
				@Override
				public void run() {
					synchronized (startupShutdownMonitor) {
						doClose();
					}
				}
			};
			Runtime.getRuntime().addShutdownHook(this.shutdownHook);
		}
	}

 

2.4 Run As – Java Application運行 – kill -9 pid關閉

       我們使用Run As – Java Application的方式進行運行我們的Application,啓動成功之後,kill -9 pid。

       此時控制檯不會執行preDestroy方法。

 

2.5 actuator:post shutdown

       還記得我們在準備工作的時候,添加了actuator,這個會暴露出來一個/actuator/shutdown的地址,我們就可以請求一下:

curl -X POST  http://localhost:8080/actuator/shutdown

 

注意:是post方法,而且post這裏要大寫、大寫、大寫,重要的事情說3遍,否則會報錯:

{
	"timestamp": "2020-05-11T07:37:55.154+0000",
	"status": 405,
	"error": "Method Not Allowed",
	"message": "Request method 'post' not supported",
	"path": "/actuator/shutdown"
}

 

請求成功的話,會返回:

{"message":"Shutting down, bye..."}

 

       此時控制檯會執行preDestroy方法。

 

2.6 ApplicationContext.close()

       這種方式的思路就是獲取到ApplicationContext,然後調用它的close()方法。

@RestController
public class ShutDownController implements ApplicationContextAware {

	private ApplicationContext applicationContext;
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
	
	@RequestMapping("/shutdownByCTX")
    public String shutdownByCTX(){
        ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) applicationContext;
        ctx.close();
        return "context is shutdown!!!";
    }

}

 

說明:編寫一個類實現ApplicationContextAware,主要是要獲取上下文ApplicationContext;然後我們暴露一個請求/shutdownByCTX,在此方法使用applicationContext的close方法。

       測試,可以通過瀏覽器訪問地址:

http://127.0.0.1:8080/shutdownByCTX

 

注意:當請求/shutdownByCTX的話,並不能看到返回值,而是:

curl: (52) Empty reply from server

              此時控制檯會執行preDestroy方法。

 

2.7 SpringApplication.exit()

       利用SpringApplication提供的exit方法:

	@RequestMapping("/shutdownBySpringApplication")
    public String shutdownBySpringApplication(){
		SpringApplication.exit(applicationContext,(ExitCodeGenerator)()->0);
        return "context is shutdown!!!";
    }

 

此時控制檯會執行preDestroy方法。

 

 

2.8 pid | xargs kill

       此方式是將pid寫入到文件,然後使用xargs kill進行關閉服務。

	public static void main(String[] args) {
		SpringApplication application = new SpringApplication(SpringBootShutdownDemoApplication.class);
		
		// 指定一個文件,寫入pid號
        application.addListeners(new ApplicationPidFileWriter("/data/tmp/app.pid"));
        application.run(args);
	}

 

       通過命令 cat /data/tmp/app.pid | xargs kill 命令直接停止服務。

此時控制檯會執行preDestroy方法。

 

三、悟纖小結

悟纖:師傅,你今天這節課講的有點亂吶?是不是來事了?

 

師傅:徒兒,看來你今天沒有挨抽,不舒服,是吧?

 

悟纖:師傅,那怪徒兒太笨,沒消化的了嘛。

 

師傅:那好,你按照自己的理解,來和大家梳理下。

 

 

小結下:

一、通過應用暴露shutdown

(1)actutor的/actutor/shutdown方法(需要配置):此時控制檯會執行preDestroy方法。

(2)利用Application的close()方法(需要編碼暴露出一個訪問的方法):此時控制檯會執行preDestroy方法。

(3)利用SpingApplication的exit()方法(需要編碼暴露出一個訪問的方法):此時控制檯會執行preDestroy方法。

 

二、kill

(1)kill -9 pid:不需要編碼,也不需要配置,利用操作系統的強制關閉經常指令;此時控制檯不會執行preDestroy方法。

(2)kill -15 pid:不需要編碼,也不需要配置,利用操作系統的強制關閉經常指令;此時控制檯會執行preDestroy方法。

(3)kill | xargs kill:需要編碼,然後利用操作系統的指令cat /data/tmp/app.pid | xargs kill;此時控制檯會執行preDestroy方法。

 

四、生產環境怎麼玩吶?

       在生產環境,我們是這麼玩的?

編寫一個sh腳本,通過grep查找到我們項目的pid,然後先使用kill -15 pid,然後sleep一下,然後在超找pid , 如果沒找到,說明已經關閉了,如果找到了,說明關閉失敗了,那麼就使用kill -9 pid強制關閉進程了。

 

我就是我,是顏色不一樣的煙火。
我就是我,是與衆不同的小蘋果。

學院中有Spring Boot相關的課程:

à悟空學院:https://t.cn/Rg3fKJD

SpringBoot視頻:http://t.cn/A6ZagYTi

Spring Cloud視頻:http://t.cn/A6ZagxSR

SpringBoot Shiro視頻:http://t.cn/A6Zag7IV

SpringBoot交流平臺:https://t.cn/R3QDhU0

SpringData和JPA視頻:http://t.cn/A6Zad1OH

SpringSecurity5.0視頻:http://t.cn/A6ZadMBe

Sharding-JDBC分庫分表實戰:http://t.cn/A6ZarrqS

分佈式事務解決方案「手寫代碼」:http://t.cn/A6ZaBnIr

JVM內存模型和性能調優:http://t.cn/A6wWMVqG

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章