一、微服務(wù)應(yīng)用上/下線發(fā)布過(guò)程中存在的問(wèn)題
在應(yīng)用上下線發(fā)布過(guò)程中,如何做到流量的無(wú)損上/下線,是一個(gè)系統(tǒng)能保證 SLA 的關(guān)鍵。如果應(yīng)用上下線不平滑,就會(huì)出現(xiàn)短時(shí)間的服務(wù)調(diào)用報(bào)錯(cuò),比如連接被拒絕、請(qǐng)求超時(shí)、沒(méi)有實(shí)例和請(qǐng)求異常等問(wèn)題。
1.1 上線過(guò)程中的問(wèn)題
在應(yīng)用上線發(fā)布過(guò)程中,由于過(guò)早暴露服務(wù),實(shí)例可能仍處在 JVMJIT 編譯或者使用的中間件還在加載,若此時(shí)大量流量進(jìn)入,可能會(huì)瞬間壓垮新起的服務(wù)實(shí)例。我們?cè)趯?shí)際場(chǎng)景中,曾經(jīng)遇到 provider 服務(wù)啟動(dòng)后,但是數(shù)據(jù)庫(kù)連接出現(xiàn)異常,未做好啟動(dòng)前的資源準(zhǔn)備,導(dǎo)致該 provider 服務(wù)在注冊(cè)中心暴露后 DB 異常還未修復(fù),無(wú)法正常提供被 consumer 調(diào)用的能力,導(dǎo)致大量請(qǐng)求異常返回。如下圖日志所示,應(yīng)用初始化時(shí),DB 連接失敗(該服務(wù)對(duì) DB 是弱依賴)。
1.2 下線過(guò)程中的問(wèn)題
在應(yīng)用下線過(guò)程中,服務(wù)消費(fèi)者感知服務(wù)提供者下線有延遲,在一段時(shí)間內(nèi),被路由到已下線服務(wù)提供者實(shí)例的請(qǐng)求都拋連接被拒絕異常。其次服務(wù)實(shí)例在接收到 SIGKILL 信號(hào)時(shí),會(huì)立即關(guān)閉,但是這時(shí)候可能在請(qǐng)求隊(duì)列中存在一部分請(qǐng)求還在處理,如果立即關(guān)閉這些請(qǐng)求都會(huì)損失掉。實(shí)際應(yīng)用中,我們?cè)诃h(huán)境上部署了 provider 的唯一一個(gè)實(shí)例,該服務(wù)被 consumer 調(diào)用,然后再執(zhí)行 kill-9強(qiáng)殺應(yīng)用 provider 的唯一實(shí)例后,服務(wù)進(jìn)程實(shí)際上已經(jīng)被終止,但是服務(wù)的注冊(cè)信息還會(huì)在注冊(cè)中心(該場(chǎng)景使用的是 ServiceComb)保留一段時(shí)間,未及時(shí)清除,如下圖所示。若此時(shí)消費(fèi)者服務(wù) consumer 調(diào)到該實(shí)例會(huì)報(bào)連接拒絕錯(cuò)誤。因?yàn)橄M(fèi)者 consumer 服務(wù)還能發(fā)現(xiàn)該實(shí)例,獲取其 IP 和端口嘗試去調(diào)用,但是該 provider 服務(wù)實(shí)例其實(shí)已經(jīng)被銷毀了。
二、如何處理應(yīng)用上/下線問(wèn)題
那么有哪些優(yōu)化措施,可以減少應(yīng)用上/下線中流量的損失?
2.1 處理應(yīng)用上線問(wèn)題
應(yīng)用上線發(fā)布主要問(wèn)題是:其中一個(gè)原因是注冊(cè)太早,過(guò)早的暴露了服務(wù);另一個(gè)原因是一些應(yīng)用初始化緩慢,若遇到大量流量,應(yīng)用容易宕機(jī)。可以采取以下優(yōu)化措施:
1.延遲注冊(cè):微服務(wù)應(yīng)用可以采用延遲注冊(cè)的方式,即在應(yīng)用啟動(dòng)之后一定時(shí)間再進(jìn)行注冊(cè)。這樣可以確保應(yīng)用完全就緒后再注冊(cè),避免了服務(wù)未就緒就被外部訪問(wèn)的情況。
2.健康檢查:微服務(wù)應(yīng)用可以實(shí)現(xiàn)健康檢查接口,通過(guò)該接口可以檢查服務(wù)是否就緒。注冊(cè)中心可以通過(guò)定期調(diào)用該接口來(lái)判斷服務(wù)是否可以對(duì)外提供服務(wù),從而避免了服務(wù)未就緒就被外部訪問(wèn)的情況。
3.預(yù)熱:對(duì)新實(shí)例進(jìn)行預(yù)熱,而不是突然將所有流量轉(zhuǎn)移到新實(shí)例上,從而避免新實(shí)例遇到大量流量,應(yīng)用容易宕機(jī)的情況。
4.啟動(dòng)優(yōu)化:對(duì)于整個(gè)服務(wù)啟動(dòng)的過(guò)程,可以進(jìn)行一些優(yōu)化措施,比如減少不必要的依賴、調(diào)整啟動(dòng)順序等,從而加快服務(wù)啟動(dòng)速度。
2.2 應(yīng)用合理的上線過(guò)程
合理的應(yīng)用上線大致分為這樣一個(gè)過(guò)程:當(dāng)應(yīng)用啟動(dòng)后,通過(guò)設(shè)置延遲注冊(cè)時(shí)間(服務(wù)對(duì)外暴露的時(shí)間)確保應(yīng)用多久后可提供服務(wù),其次可依賴平臺(tái)檢查服務(wù)的就緒狀態(tài)(比如 K8S 的就緒探針)確保服務(wù)對(duì)外提供服務(wù)為就緒狀態(tài),然后通過(guò)預(yù)熱對(duì)剛啟動(dòng)應(yīng)用進(jìn)行保護(hù),確保流量慢慢進(jìn)入剛啟動(dòng)的應(yīng)用,最后流量逐漸增到正常情況。
2.3 處理應(yīng)用下線問(wèn)題
應(yīng)用下線過(guò)程最主要問(wèn)題是:消費(fèi)者應(yīng)用無(wú)法及時(shí)感知到注冊(cè)中心列表的刷新,導(dǎo)致可能還有新流量訪問(wèn)下線應(yīng)用。可以采取以下優(yōu)化措施:
1.減少注冊(cè)中心緩存時(shí)間:將注冊(cè)中心中服務(wù)列表的緩存時(shí)間縮短,可以使消費(fèi)者應(yīng)用更快地獲取到服務(wù)列表的最新信息。這樣可以減少因服務(wù)列表緩存而導(dǎo)致的訪問(wèn)下線應(yīng)用的流量。
2.實(shí)時(shí)性優(yōu)化:在服務(wù)消費(fèi)者和注冊(cè)中心之間使用長(zhǎng)連接、實(shí)時(shí)通知等機(jī)制,從而能夠?qū)崟r(shí)獲取注冊(cè)中心中服務(wù)列表的變化。
3.實(shí)現(xiàn)熔斷機(jī)制:在消費(fèi)者應(yīng)用中實(shí)現(xiàn)熔斷機(jī)制,當(dāng)某個(gè)服務(wù)實(shí)例出現(xiàn)故障或不可用時(shí),可以快速切換到其他可用的服務(wù)實(shí)例。這樣可以避免將流量發(fā)送到已下線的應(yīng)用程序上,并確保消費(fèi)者應(yīng)用的可用性。
2.4 應(yīng)用合理的下線過(guò)程
合理的應(yīng)用下線大致分為這樣一個(gè)過(guò)程:當(dāng)應(yīng)用接受到外部的關(guān)閉(停止服務(wù))請(qǐng)求后,不能在接收新的業(yè)務(wù)請(qǐng)求,但是會(huì)存在一些正在處理的業(yè)務(wù)請(qǐng)求,需等這些請(qǐng)求處理完后再銷毀應(yīng)用使用的資源,最后就可以通知主進(jìn)程退出。
三、應(yīng)用下線注意點(diǎn)
針對(duì)應(yīng)用下線在虛機(jī)場(chǎng)景和容器場(chǎng)景需要關(guān)注一些注意點(diǎn)。
3.1 虛機(jī)場(chǎng)景
當(dāng)我們要關(guān)閉虛擬機(jī)應(yīng)用時(shí),我們一般會(huì)使用 ps-ef|grepxxx 查找到進(jìn)程 ID,然后再執(zhí)行 kill-9PID 操作。
kill命令使用科普:
1.kill-9,系統(tǒng)會(huì)發(fā)出 SIGKILL(9)信號(hào),由操作系統(tǒng)內(nèi)核完成殺進(jìn)程操作,該信號(hào)不允許忽略和阻塞,應(yīng)用程序會(huì)立即終止(強(qiáng)制殺死)。
2.kill-15,默認(rèn)使用信號(hào),系統(tǒng)向應(yīng)用發(fā)送 SIGTERM(15)信號(hào),給目標(biāo)進(jìn)程一個(gè)清理善后工作的機(jī)會(huì)是一種優(yōu)雅終止進(jìn)程的方式,告訴進(jìn)程需要停止運(yùn)行并開始清理資源。
因?yàn)?kill-9PID 會(huì)強(qiáng)制殺死應(yīng)用,以合理的應(yīng)用下線流程看,應(yīng)需處理完相關(guān)舊業(yè)務(wù)請(qǐng)求,清理相關(guān)資源后再退出進(jìn)程,所以當(dāng)要關(guān)閉虛擬機(jī)應(yīng)用時(shí),請(qǐng)執(zhí)行 killPID——以優(yōu)雅的方式停止運(yùn)行。
3.2 容器場(chǎng)景
Kubernetes 目前是業(yè)界容器編排領(lǐng)域的事實(shí)標(biāo)準(zhǔn),業(yè)界一般默認(rèn)都是用 K8S 來(lái)管理容器。K8S 提供了 Pod 優(yōu)雅退出機(jī)制,允許 Pod 在退出前完成一些清理工作。preStop 會(huì)先執(zhí)行完,然后 K8S 才會(huì)給 Pod 發(fā)送 TERM 信號(hào)。在容器場(chǎng)景利用 K8S 提供的 preStop 機(jī)制,配合延遲下線 API 使用,這樣就能保證流量的無(wú)損下線。
...
spec:
-name:lifecycle-demo-container
image:nginx
lifecycle:
preStop:
exec:
command:["/bin/sh","-c","todoxxx;dosleep30;done"]
...
(1)為什么容器應(yīng)用(K8S 環(huán)境)要配置 preStop?首先要介紹一下 Pod 的終止過(guò)程。
參考:https://kubernetes.renkeju.com/chapter_4/4.5.5.pod_termination_process.html
1.用戶發(fā)送刪除Pod對(duì)象的命令。
2.API服務(wù)器中的Pod對(duì)象會(huì)隨著時(shí)間的推移而更新,在寬限期內(nèi)(默認(rèn)為 30 秒),Pod 被視為“dead”。
3.將Pod標(biāo)記為“Terminating”狀態(tài)。
4.(與第 3 步同時(shí)運(yùn)行)kubelet在監(jiān)控到Pod對(duì)象轉(zhuǎn)為“Terminating”狀態(tài)的同時(shí)啟動(dòng)Pod關(guān)閉程序。
5.(與第 3 步同時(shí)運(yùn)行)端點(diǎn)控制器監(jiān)控到Pod對(duì)象的關(guān)閉行為時(shí)將其從所有匹配到此端點(diǎn)的Service資源的端點(diǎn)列表中移除。
6.Pod對(duì)象中的容器進(jìn)程收到TERM信號(hào)。
7.如果當(dāng)前當(dāng)前Pod對(duì)象定義了preStop鉤子處理器,則在其標(biāo)記為“Terminating”后即會(huì)以同步的方式啟動(dòng)執(zhí)行;如若寬限期結(jié)束后,preStop仍未執(zhí)行結(jié)束,則第 2 步會(huì)被重新執(zhí)行并額外獲取一個(gè)時(shí)長(zhǎng)為 2 秒的小寬限期。
8.寬限期結(jié)束后,若存在任何一個(gè)仍在運(yùn)行的進(jìn)程,那么Pod對(duì)象即會(huì)收到SIGKILL信號(hào)。
9.kubelet請(qǐng)求APIServer將此Pod資源的寬限期設(shè)置為 0 從而完成刪除操作,它變得對(duì)用戶不在可見(jiàn)。
默認(rèn)情況下,所有刪除操作的寬限期都是 30 秒,不過(guò),kubectldelete命令可以使用“--grace-period=”選項(xiàng)自定義其時(shí)長(zhǎng),若使用 0 值則表示直接強(qiáng)制刪除指定的資源,不過(guò),此時(shí)需要同時(shí)為命令使用“--force”選項(xiàng)。
從上述 Pod 終止過(guò)程的時(shí)序圖可知,關(guān)閉 Pod 流程(關(guān)注紅色框),給 Pod 內(nèi)的進(jìn)程發(fā)送 TERM 信號(hào)(即 kill,kill-15),如果配置了 preStop 鉤子也會(huì)同時(shí)處理,最后寬限期結(jié)束后,若存在任何一個(gè)仍在運(yùn)行的進(jìn)程,那么 Pod 對(duì)象即會(huì)收到 SIGKILL(kill-9)信號(hào)。
(2)存在這樣一種情況 Pod 中的業(yè)務(wù)進(jìn)程接受不到 SIGTERM 信號(hào)
存在這樣一種情況 Pod 中的業(yè)務(wù)進(jìn)程接受不到 SIGTERM 信號(hào)(而且沒(méi)有配置 preStop 鉤子),等待一段時(shí)間業(yè)務(wù)進(jìn)程直接被 SIGKILL 強(qiáng)制殺死了。
為什么業(yè)務(wù)進(jìn)程接受不到 SIGTERM 信號(hào)?
通常都是因?yàn)槿萜鲉?dòng)入口使用了shell,比如使用了類似/bin/sh-cmy-app或/docker-entrypoint.sh這樣的ENTRYPOINT或CMD,這就可能就會(huì)導(dǎo)致容器內(nèi)的業(yè)務(wù)進(jìn)程收不到 SIGTERM 信號(hào),原因是:
1.容器主進(jìn)程是 shell,業(yè)務(wù)進(jìn)程是在 shell 中啟動(dòng)的,成為了 shell 進(jìn)程的子進(jìn)程。
2.shell進(jìn)程默認(rèn)不會(huì)處理SIGTERM信號(hào),自己不會(huì)退出,也不會(huì)將信號(hào)傳遞給子進(jìn)程,導(dǎo)致業(yè)務(wù)進(jìn)程不會(huì)觸發(fā)停止邏輯。
3.當(dāng)?shù)鹊終8S優(yōu)雅停止超時(shí)時(shí)間(terminationGracePeriodSeconds,默認(rèn) 30s),發(fā)送 SIGKILL 強(qiáng)制殺死 shell 及其子進(jìn)程。
(3)如何解決上述 Pod 中的業(yè)務(wù)進(jìn)程接收不到 SIGTERM 信號(hào)問(wèn)題
1.配置 preStop 鉤子(K8S 場(chǎng)景),處理退出前完成一些清理工作,比如使用無(wú)損上下線插件的應(yīng)用服務(wù)需在停止前通知實(shí)例進(jìn)行下線。
2.如果可以的話,盡量不使用shell啟動(dòng)業(yè)務(wù)進(jìn)程。
3.如果一定要通過(guò)shell啟動(dòng),比如在啟動(dòng)前需要用shell進(jìn)程一些判斷和處理,或者需要啟動(dòng)多個(gè)進(jìn)程,那么就需要在shell中傳遞下SIGTERM信號(hào)了。
所以容器應(yīng)用(K8S 環(huán)境)要配置 preStop,在停止前通知實(shí)例進(jìn)行下線,加了一層防護(hù),保證 Pod 中的業(yè)務(wù)能優(yōu)雅的結(jié)束。
四、Sermant 如何解決應(yīng)用上/下線問(wèn)題
針對(duì)應(yīng)用上下線發(fā)布過(guò)程中的問(wèn)題,Sermant 插件提供預(yù)熱和延遲下線機(jī)制,為應(yīng)用提供無(wú)損上下線的能力。預(yù)熱是無(wú)損上線的核心機(jī)制,延遲下線是無(wú)損下線的核心機(jī)制,而且為了無(wú)損上線,還做了延遲注冊(cè)機(jī)制。
4.1 上線問(wèn)題的解決方式
延遲注冊(cè):若服務(wù)還未完全初始化就已經(jīng)注冊(cè)到注冊(cè)中心提供給消費(fèi)者調(diào)用,很有可能因資源為加載完成導(dǎo)致請(qǐng)求報(bào)錯(cuò)。可以通過(guò)設(shè)置延遲注冊(cè),讓服務(wù)充分初始化后再注冊(cè)到注冊(cè)中心對(duì)外提供服務(wù)。
預(yù)熱:是基于客戶端實(shí)現(xiàn)的,當(dāng)流量進(jìn)入時(shí),Sermant 會(huì)動(dòng)態(tài)調(diào)整流量,根據(jù)服務(wù)的預(yù)熱配置,對(duì)流量進(jìn)行動(dòng)態(tài)分配。對(duì)于開啟服務(wù)預(yù)熱的實(shí)例,在剛啟動(dòng)時(shí),相對(duì)于其他已啟動(dòng)的實(shí)例,分配的流量會(huì)更少,流量將以曲線方式隨時(shí)間推移增加直至與其他實(shí)例近乎持平。目的是采用少流量對(duì)服務(wù)實(shí)例進(jìn)行初始化,防止服務(wù)崩潰。
4.2 下線問(wèn)題的解決方式
上圖描述了 Sermant 是如何解決服務(wù)下線問(wèn)題的:
0.微服務(wù)應(yīng)用 consumerA、providerA、consumerB、providerB 攜帶 Sermant 啟動(dòng),并將相關(guān) ip:port 等信息注冊(cè)到注冊(cè)中心;
1.微服務(wù)應(yīng)用 consumerA 可以正常調(diào)用 providerA 和 providerB;
2.若要重啟 providerA,providerA 會(huì)標(biāo)記自身將下線(通知注冊(cè)中心將下線),并開始統(tǒng)計(jì)請(qǐng)求確保當(dāng)前請(qǐng)求已全部處理完成;
3.providerA 會(huì)通知其上游應(yīng)用其自身的下線信息;
4.consumerA 接受到 providerA 下線信息后,將其從緩存實(shí)例列表移除;
5.providerA 在處理完當(dāng)前的所有請(qǐng)求后,即可重啟。
總的來(lái)說(shuō),Sermant 對(duì)于服務(wù)下線的機(jī)制概括為:
延遲下線:即對(duì)下線的實(shí)例提供保護(hù),插件基于下線實(shí)時(shí)通知+刷新緩存的機(jī)制快速更新上游的實(shí)例緩存,同時(shí)基于流量統(tǒng)計(jì)的方式,確保即將下線的實(shí)例盡可能的將流量處理完成,最大程度避免流量丟失。提供了延遲下線 API,方便在 K8S 環(huán)境中配置 preStop。
流量統(tǒng)計(jì):為確保當(dāng)前請(qǐng)求已全部處理完成,在服務(wù)下線時(shí),Sermant 會(huì)嘗試等待 30s(可配置),定時(shí)統(tǒng)計(jì)和判斷當(dāng)前實(shí)例請(qǐng)求是否均處理完成,處理完成后最終下線。
五、總結(jié)
Sermant 插件為微服務(wù)應(yīng)用提供無(wú)損上下線的能力,若要下線應(yīng)用,針對(duì)虛擬場(chǎng)景,請(qǐng)使用 killPID;針對(duì)容器場(chǎng)景(K8S 環(huán)境),請(qǐng)配置 preStop 鉤子。
Sermant作為專注于服務(wù)治理領(lǐng)域的字節(jié)碼增強(qiáng)框架,致力于提供高性能、可擴(kuò)展、易接入、功能豐富的服務(wù)治理體驗(yàn),并會(huì)在每個(gè)版本中做好性能、功能、體驗(yàn)的看護(hù)。
編輯:黃飛
-
容器
+關(guān)注
關(guān)注
0文章
499瀏覽量
22125 -
華為云
+關(guān)注
關(guān)注
3文章
2691瀏覽量
17588
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
使用阿里云ACM簡(jiǎn)化你的Spring Cloud微服務(wù)環(huán)境配置管理
微服務(wù)架構(gòu)和CQRS架構(gòu)基本概念介紹
微服務(wù)網(wǎng)關(guān)gateway的相關(guān)資料推薦
云芯一號(hào)ARM微服務(wù)器板卡的方法和過(guò)程介紹
java微服務(wù)生態(tài)系統(tǒng)模型解讀
![java<b class='flag-5'>微服務(wù)</b>生態(tài)系統(tǒng)模型解讀](https://file.elecfans.com/web2/M00/49/E0/pYYBAGKhvG6AaXvEAABXhnr305Q375.png)
微服務(wù)與容器技術(shù)實(shí)踐
![<b class='flag-5'>微服務(wù)</b>與容器技術(shù)實(shí)踐](https://file.elecfans.com/web2/M00/49/F5/pYYBAGKhvH2AboZGAAA8A17xTso883.png)
微服務(wù)優(yōu)勢(shì)_微服務(wù)架構(gòu)的好處與不足
什么是微服務(wù)和容器?微服務(wù)和容器的作用是什么
![什么是<b class='flag-5'>微服務(wù)</b>和容器?<b class='flag-5'>微服務(wù)</b>和容器的作用是什么](https://file.elecfans.com/web1/M00/82/56/pIYBAFw6qRKAdVsxAAAPUgnbtIQ592.png)
什么是微服務(wù)架構(gòu)_微服務(wù)架構(gòu)的優(yōu)缺點(diǎn)及應(yīng)用
![什么是<b class='flag-5'>微服務(wù)</b>架構(gòu)_<b class='flag-5'>微服務(wù)</b>架構(gòu)的優(yōu)缺點(diǎn)及應(yīng)用](https://file.elecfans.com/web1/M00/95/2D/pIYBAFzzLoSAMb0hAAAUYDQiYxU355.jpg)
微服務(wù)使用失敗的原因有什么
微服務(wù)架構(gòu)中的服務(wù)之間如何互相調(diào)用呢?
springcloud微服務(wù)架構(gòu)
docker微服務(wù)架構(gòu)實(shí)戰(zhàn)
設(shè)計(jì)微服務(wù)架構(gòu)的原則
![設(shè)計(jì)<b class='flag-5'>微服務(wù)</b>架構(gòu)的原則](https://file.elecfans.com/web2/M00/3F/D7/poYBAGJqPMKAEXjWAAAOpepuZJ8475.jpg)
評(píng)論