1 概念
優雅停機是什么?網上說的優雅下線、無損下線,都是一個意思。
優雅停機,通常是指在設備、系統或應用程序中止運作前,先執行一定的流程或動作,以確保數據的安全、預防錯誤并保證系統的整體穩定。
一般來說,優雅停機可以參考以下步驟以實現:
備份數據 :立即將內存中的所有未保存的修改、緩存等數據保存到數據庫或磁盤中。
停止接收新的請求
處理未完成的請求
通知其他依賴組件
等待所有要素安全退出后,關閉系統
在具體實施時,不同的設備、不同的系統、不同的應用,所需要的優雅停機步驟也不盡相同,甚至需要根據不同的場景來選擇不同的方法。
例如,在某些情況下,你可能需要讓用戶知道,系統即將關閉,并告訴他們應當保存所有的工作并退出系統;而在另一些情況下,你可能需要設計一種策略,能夠讓系統在無用戶介入的情況下,自動保存所有的狀態,并在下次啟動時恢復之。
但是,無論在哪種情況下,優雅停機的目標都是保護數據,避免錯誤,并盡量減少到訪用戶或使用者的不便。
上面的步驟,其實還缺了不少基礎的內容,比如,停止請求外,還要停止接收定時任務、停止接收mq消息,等待他們的完成,這2項都是我們微服務中必不可缺的能力。
因此,我希望通過本文,能夠更清晰,更詳細的講解,在我已知的真實業務場景下,如何做優雅停機。
文中,很多內容不會講得太詳細,需要大家有一定的搜索能力或者經驗!
2 用案例說話
隨著微服務的興起,運維方式由docker -> k8s 變化,優雅停機涉及到的點就越來越多!下面,我們用一個案例,說明優雅停機中的問題和問題解決方案。
案例前:k8s 停機流程
當程序員執行 kubectl delete pod 命令時,兩個過程開始:
網絡規則即將生效:
Kube-apiserver 收到 pod 刪除請求,并將 pod 的狀態更新為 Extinating at Etcd;
終結點控制器從終結點對象中刪除 Pod 的 IP;
Kuber-proxy 根據 Endpoint 對象的更改更新 iptables 的規則,并且不再將流量路由到已刪除的 pod。
刪除容器:
Kube-apiserver 收到 pod 刪除請求,并將 pod 的狀態更新為 Extinating at Etcd;
Kubelet 清理節點處的容器相關資源,如存儲、網絡;
添加 Prestop hook 鉤子,等待流量不再發給pod;
Kubelet 將SIGTERM發送到容器;
如果容器在默認的 30 秒內沒有退出,Kubelet 將發送 SIGKILL 并強制其退出。
k8s + springboot + nacos 案例
PreStopHook 做了2件事情:
nacos反注冊
休眠35秒
通過信號量關閉springboot程序;
其中,k8s的 terminationGracePeriodSeconds(寬限期)設置為35s。
問題
springBoot程序關閉時間只有2s, 那么該程序就無法處理完一些線程任務、異步消息、定時任務等。為什么呢?
寬限期設置了35s,PreStop休眠了35s + 一個請求的時間,超過了寬限期,那么 kubelet 就會給與 pod 增加一次性2s的寬限時間。Pod 的生命周期,2s不管程序是否正常結束,都會被Kill -9。
為什么反注冊之后需要休眠35s?
這里涉及到nacos服務發現原理,nacos服務變更響應時間:實時;ribbon 默認緩存刷新時間30s;因此,一開始是設置30s的,發現還有feign請求失敗的情況,所以設置成了35s以解決這個問題!
nacos服務變更響應時間真的是實時嗎?
其實并不一定,nacos服務發現是通過http和udp實現的,udp是實時的,http最大等待時間是10s,但是,udp端口生產環境可能沒有開放!所以,案例中的nacos服務發現僅通過http定時輪詢實現。
案例優化
上面的案例可以優化的點
nacos 反注冊后休眠35s,是否可以減少;
terminationGracePeriodSeconds 設置多少合理?
優化點1
反注冊后休眠的35s時候受到nacos服務發現 + ribbon 緩存刷新時間影響,正常應該是 服務發現時間 + 緩存刷新時間 40s才能在極端情況下保證服務停機時,不會再有feign 請求進入。
如果想要縮短這個時間
啟用udp,這個需要和運維同學商量,否則10s等待少不了;
監聽nacos服務變更通知,發現服務下線后,及時刷新ribbon緩存;
/** *訂閱nacos實例變更通知 *手動刷新ribbon服務實例緩存 *nacosclient1.4.6【1.4.1有重大缺陷,要注意】 */ @Component @Slf4j publicclassNacosInstancesChangeEventListenerextendsSubscriber{ @Resource privateSpringClientFactoryspringClientFactory; @PostConstruct publicvoidregisterToNotifyCenter(){ NotifyCenter.registerSubscriber(this); } @Override publicvoidonEvent(InstancesChangeEventevent){ Stringservice=event.getServiceName(); //service:DEFAULT_GROUP@@demoribbonService:demo StringribbonService=service.substring(service.indexOf("@@")+2); log.info("####接收到微服務nacos實例變更事件:{}ribbonServiceName:{}",event.getServiceName(),ribbonService); ILoadBalancerloadBalancer=springClientFactory.getLoadBalancer(ribbonService); if(loadBalancer!=null){ ((ZoneAwareLoadBalancer>)loadBalancer).updateListOfServers(); log.info("刷新 ribbon 服務實例:{}緩存成功",ribbonService); } } @Override publicClass?extends?com.alibaba.nacos.common.notify.Event>subscribeType(){ returnInstancesChangeEvent.class; } /** *nacos1.4.4~1.4.6需要加這個方法的實現,2.1.2以后版本修復了該問題 *多注冊中心時,變更事件沒有隔離,因此需要實現該方法來判斷事件是否需要處理 *@seeISSUE#8428-NacosInstancesChangeEventScope ***/ @Override publicbooleanscopeMatches(InstancesChangeEventevent){ returntrue; } }
優化點2
terminationGracePeriodSeconds 的值應該略大于 PreStop耗時 + springBoot 停機時間,springBoot 停機時間是由程序業務決定的(mq消息、定時任務、線程池任務、以及備份數據),網上的推薦做法是啟用springBoot的優雅停機功能,并實現自定義的關閉邏輯。
springBoot優雅停機的默認緩沖時間是30s,因此,terminationGracePeriodSeconds的時間個人建議10 + 30s即可。
經過優化后
使用 actuator shutdown 方案
有些網貼推薦使用 actuator shutdown 進行優雅停機,那么看下其流程圖:
其實,真正的情況并非如上圖所示,因為調用shutdown后,springBoot就會進入優雅停機流程,但是這個流程沒有結束,然后就會被kill -15 中斷,如果線程池沒有做好配置,線程池任務沒有結束,服務就會關閉。
//沒有設置下面參數,在kill-15時,線程池沒有執行結束,會被強制關閉 threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); threadPoolTaskExecutor.setAwaitTerminationSeconds(30);
3 再次優化
mq 和 定時任務
上面的方案中,提到nacos反注冊時,其他服務監聽反注冊事件,進行ribbon緩存刷新,那么,反注冊的服務(停機服務)自身,是否可以也監聽該事件呢?答案是可以的。
停機的服務監聽nacos反注冊事件,判斷是自己反注冊了,表示準備關機,那么就可以停止對mq消息的監聽,停止定時任務,這樣就比在優雅停機時,進行mq 和 定時任務的停止更完美。
流量控制
如果沒有使用k8s進行pod節點的流量控制,那么大概率會使用 springCloud gateway作為服務網關,因此,gateway 服務也應該監聽nacos的反注冊事件,從而及時刷新ribbon的緩存,關閉停機服務的流量。
4 小結
經過大量的資料參考、學習,最終得到的一份自己認為合格的優雅停機方案,里面可能有較多的不專業表述,敬請諒解和指正,謝謝。
在本文的最后,還要說下,優雅停機最大的挑戰并不是來源于這個優雅停機流程,機械化的流程前人都幫忙躺過了,剩下的是業務服務自身的邏輯:
有沒有包含超過30s的業務邏輯,如執行超過30s的請求,定時任務、線程池任務或mq消息;
服務關閉時,如何保存未完成的任務、數據,實現自定義的關閉邏輯;
接口邏輯是否做了冪等;
審核編輯:劉清
-
控制器
+關注
關注
112文章
15444瀏覽量
173100 -
UDP通信
+關注
關注
0文章
21瀏覽量
1849 -
nacos
+關注
關注
0文章
10瀏覽量
170
原文標題:SpringBoot + Nacos + k8s 優雅停機
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論