優雅停機方案

概述

線上系統發佈重啓過程中保證業務的連續性,讓服務的客戶端無感知。實現這種目標的停機我們稱之爲優雅停機(Graceful Shutdown),這樣的發佈部署叫平滑發佈(Gentle Deploment).

線上重啓面臨的問題

系統間調用模型
系統間調用模型

常見問題

  • 重啓tomcat將導致處理中的請求丟失
  • 使用kill -15重啓tomcat,如果服務一直沒有成功關閉,將導致不斷有新的請求進入並被掛起,最終失敗.

解決方案思路

  1. 要做到應用容器停止不影響正在執行的業務.
  2. 負載均衡組件能自動感知服務節點下線和上線,新請求不再分發到舊服務.
  3. 對無法處理的請求嘗試重試補償.

優雅停機相關知識

Linux中斷

在Linux/unix 下,中止一個Java進程有兩種方式,一種是 kill -9 pid,一種是 kill -15 pill(默認)。

  • SIGNKILL(9) 的效果是立即殺死進程。 該信號不能被阻塞, 處理和忽略。
  • SIGNTERM(15) 的效果是正常退出進程,退出前可以被阻塞或回調處理。並且它是 Linux 缺省的程序中斷信號。

ShutdownHook

JDK提供了Java.Runtime.addShutdownHook(Thread hook)方法,可以註冊一個JVM關閉的鉤子,它是一個回調函數.在一下幾種場景中被調用:

  • 程序正常退出
  • 使用System.exit()
  • 終端使用Ctrl+C觸發的中斷
  • 系統關閉
  • OutOfMemory宕機
  • 使用Kill pid命令幹掉進程

SpringCloud對優雅停機的處理機制

SpringBoot ApplicationContext生命週期

SpringBoot Application在啓動時註冊了ShutdownHook,在doClose()中聲明瞭在ShutdownHook對DisposableBean進行關閉.
org.springframework.context.support.AbstractApplicationContext.registerShutdownHook()
org.springframework.context.support.AbstractApplicationContext.doClose()

Spring Bean生命週期

Spring框架中在bean初始化和銷燬時候執行某個方法的三種實現方式。

  • 通過註解@PostConastruct 和 @PreDestroy來實現Bean初始化執行和銷燬時候執行方法;
  • 通過實現接口InitializingBean ,DisposableBean來實現Bean初始化執行和銷燬時候執行方法;
  • 通過xml配置文件中bean的init-method="" destroy-method=""來實現Bean初始化執行和銷燬時候執行方法;

服務註冊生命週期

ServiceRegistry聲明瞭spring cloud對服務服務註冊的生命週期.
通過AbstractAutoServiceRegistration.destroy()聲明瞭Application關閉時的回調動作.
@PreDestroy
AbstractAutoServiceRegistration.destroy()

EurekaClientAutoConfiguration這個自動配置類做了相應的工作。

Ribbon自動重試

Ribbon針對以下異常自動重試(HttpClientLoadBalancerErrorHandler.retriable )

  • ConnectException.class,
  • SocketTimeoutException.class
  • ConnectTimeoutException.class,
  • NoHttpResponseException.class,
  • ConnectionPoolTimeoutException.class,
  • ConnectionClosedException.class,
  • HttpHostConnectException.class);

服務發現的心跳檢測

ConsulCatalogWatch#catalogServicesWatch()聲明瞭每隔一段時間會去註冊中心檢測服務狀態,狀態異常的服務節點將在負載均衡時排除掉.

Ribbon負載均衡

Ribbon的負載均衡體現在selectServer時.
在這裏插入圖片描述

tomcat的響應機制

  • tomcat的Connections和Thread不是直接對於的關係,當線程阻塞或者線程池關閉時,依然可以建立連接.
  • 當線程池正常,線程不夠時,如果沒有達到最大線程數,將不斷新建線程.

Nginx的負載均衡策略

  • 當nginx發現upstream無法連接時,將不再分配請求到該節點
  • 如果可以建立連接,將基於負載均衡策略分發

傳統架構的優雅停機方案

使用Nginx做tomcat的負載均衡,使用JDK自帶的HttpURLConnection.

方案

  • 使用kill -15,等待30S,使用kill -9
  • 儘量保證所有請求在3S內處理完成
  • 接口調用方要設置超時參數,超時後對異常做處理
  • 接口調用方有選擇的通過http重試或者整體任務重試實現完整性

微服務架構的優雅停機方案

使用Feign+Ribbon作爲客戶端和負載均衡.

方案

  • 使用kill -15,等待10S,使用kill -9
  • 儘量保證所有請求在1S內處理完成
  • 接口調用方要設置超時參數,超時後對異常做處理
  • 縮短服務發現健康檢查間隔,建議配置在1S以內(默認1S).

參考資料

  1. 微服務部署與優雅停機
  2. 如何優雅關閉 Spring Boot 應用
  3. Spring Boot 2.0 之優雅停機
  4. nginx反向代理+負載均衡+服務器宕機解決辦法
發佈了47 篇原創文章 · 獲贊 40 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章