Netty優雅退出機制和原理

1. 引言

2. 進程的優雅退出

2.1 Kill -9 PID帶來的問題

在Linux上一般會經過kill -9 pid的方式強制將某個進程殺掉,這種方式簡單高效,所以不少程序的中止腳本常常會選擇使用kill -9 pid的方式。數據庫

不管是Linux的Kill -9 pid仍是windows的taskkill /f /pid強制進程退出,都會帶來一些反作用:對應用軟件而言其效果等同於忽然掉電,可能會致使以下一些問題:windows

  1. 緩存中的數據還沒有持久化到磁盤中,致使數據丟失;
  2. 正在進行文件的write操做,沒有更新完成,忽然退出,致使文件損壞;
  3. 線程的消息隊列中尚有接收到的請求消息還沒來得及處理,致使請求消息丟失;
  4. 數據庫操做已經完成,例如帳戶餘額更新,準備返回應答消息給客戶端時,消息尚在通訊線程的發送隊列5. 中排隊等待發送,進程強制退出致使應答消息沒有返回給客戶端,客戶端發起超時重試,會帶來重複更新問題;
  5. 其它問題等...

2.2 JAVA優雅退出

Java的優雅停機一般經過註冊JDK的ShutdownHook來實現,當系統接收到退出指令後,首先標記系統處於退出狀態,再也不接收新的消息,而後將積壓的消息處理完,最後調用資源回收接口將資源銷燬,最後各線程退出執行。緩存

一般優雅退出須要有超時控制機制,例如30S,若是到達超時時間仍然沒有完成退出前的資源回收等操做,則由停機腳本直接調用kill -9 pid,強制退出。異步

3. 如何實現Netty優雅退出

要實現Netty的優雅退出,首先須要瞭解通用Java進程的優雅退出如何實現。下面咱們先講解下優雅退出的實現原理,並結合實際代碼進行講解。最後看下如何實現Netty的優雅退出。ide

信號是在軟件層次上對中斷機制的一種模擬,在原理上,一個進程收到一個信號與處理器收到一箇中斷請求能夠說是同樣的,它oop

3.1 信號簡介

是進程間一種異步通訊的機制。以Linux的kill命令爲例,kill -s SIGKILL pid (即kill -9 pid) 當即殺死指定pid的進程,SIGKILL就是發送給pid進程的信號。測試

信號具備平臺相關性,Linux平臺支持的一些終止進程信號以下所示:操作系統

輸入圖片說明

Windows平臺存在一些差別,它的一些信號舉例以下:SIGINT(Ctrl+C中斷)、SIGILL、SIGTERM (kill發出的軟件終止)、SIGBREAK (Ctrl+Break中斷)。線程

信號選擇:爲了避免干擾正常信號的運做,又能模擬Java異步通知,在Linux上咱們須要先選定一種特殊的信號。經過查看信號列表上的描述,發現 SIGUSR1 和 SIGUSR2 是容許用戶自定義的信號,咱們能夠選擇SIGUSR2,爲了測試方便,在Windows上咱們能夠選擇SIGINT。code

3.2 Java程序的優雅退出

首先看下通用的Java進程優雅退出的流程圖:

輸入圖片說明

  • 第一步,應用進程啓動的時候,初始化Signal實例,它的代碼示例以下:
Signal sig = new Signal(getOSSignalType());
  • 第二步,根據操做系統的名稱來獲取對應的信號名稱,代碼以下:
private String getOSSignalType()
{
       return System.getProperties().getProperty("os.name").
		 toLowerCase().startsWith("win") ? "INT" : "USR2";
}

判斷是不是windows操做系統,若是是則選擇SIGINT,接收Ctrl+C中斷的指令;不然選擇USR2信號,接收SIGUSR2(等價於kill -12 pid)指令。

  • 第三步,將實例化以後的SignalHandler註冊到JDK的Signal,一旦Java進程接收到kill -12 或者 Ctrl+C則回調handle接口,代碼示例以下:
Signal.handle(sig, shutdownHandler);

其中shutdownHandler實現了SignalHandler接口的handle(Signal sgin)方法,代碼示例以下:

  • 第四步,在接收到信號回調的handle接口中,初始化JDK的ShutdownHook線程,並將其註冊到Runtime中,示例代碼以下:
private void invokeShutdownHook()
{
	Thread t = new Thread(new ShutdownHook(), "ShutdownHook-Thread");
	Runtime.getRuntime().addShutdownHook(t);
}
  • 第五步,接收到進程退出信號後,在回調的handle接口中執行虛擬機的退出操做,示例代碼以下:
Runtime.getRuntime().exit(0);

虛擬機退出時,底層會自動檢測用戶是否註冊了ShutdownHook任務,若是有,則會自動將ShutdownHook線程拉起,執行它的Run方法,用戶只須要在ShutdownHook中執行資源釋放操做便可,示例代碼以下:

class ShutdownHook implements Runnable
{
	@Override
	public void run() {
		System.out.println("ShutdownHook execute start...");
		System.out.print("Netty NioEventLoopGroup shutdownGracefully...");
		try {
			TimeUnit.SECONDS.sleep(10);//模擬應用進程退出前的處理操做
		} catch (InterruptedException e) {
				e.printStackTrace();
		}
		System.out.println("ShutdownHook execute end...");
	System.out.println("Sytem shutdown over, the cost time is 10000MS");
		}
}

下面咱們在Windows環境中對通用的Java優雅退出程序進行測試,打開CMD控制檯,拉起待測試程序,以下所示:

啓動進程:

查看線程信息,發現註冊的ShutdownHook線程沒有啓動,符合預期:

相關文章
相關標籤/搜索