要優(yōu)雅退出嗎?dubbogo給你

dubbogo

Apache Dubbo是由阿里開源的一個(gè)RPC框架邻眷,而dubbogo則是相對應(yīng)的go語言版本:

之前dubbogo一直沒有優(yōu)雅退出的機(jī)制佃声,終于有小伙伴忍不住了強(qiáng)烈要求我們實(shí)現(xiàn)這個(gè)部分。艱難摸魚了兩周之后阴绢,我才把這個(gè)搞完,該功能的PRhttps://github.com/apache/dubbo-go/pull/255

當(dāng)我們討論優(yōu)雅退出的時(shí)候鸡挠,最基本的要求是自動(dòng)無損停機(jī)。它同時(shí)強(qiáng)調(diào)了自動(dòng)和無損兩個(gè)方面搬男。

首先是自動(dòng)拣展,而與自動(dòng)對應(yīng)的則是手動(dòng)了。手工介入的缺陷是顯而易見的缔逛,它要求我們在應(yīng)用下線的時(shí)候手動(dòng)摘掉流量备埃。這一步可以通過網(wǎng)關(guān)、負(fù)載均衡或者注冊中心來實(shí)現(xiàn)褐奴。它還容易忘和出錯(cuò)按脚,如果這個(gè)東西還要求到運(yùn)維身上,那就真的是下個(gè)線都得求爺爺告奶奶敦冬,開發(fā)體驗(yàn)十分不好辅搬。

而無損,關(guān)鍵則是脖旱,正在執(zhí)行的事情要能執(zhí)行完伞辛。這個(gè)“事情”含義會(huì)非常廣泛烂翰,比如說發(fā)出去的請求我要能收回響應(yīng);收到的請求要執(zhí)行完畢并且給人家返回響應(yīng)……如果更加嚴(yán)格的來說蚤氏,那么本地啟動(dòng)的定時(shí)任務(wù)甘耿,或者分布式事務(wù),都應(yīng)該在完成之后才能關(guān)機(jī)竿滨。

設(shè)計(jì)

我們先來看一下佳恬,一般情況下關(guān)機(jī)會(huì)發(fā)生什么:

這里面可以看出來,如果沒有優(yōu)雅退出機(jī)制的話于游,服務(wù)器是很任性的毁葱,誰都不說咔嚓一下關(guān)了。

然后注冊中心隔了一小會(huì)之后贰剥,通過心跳檢測或者監(jiān)聽到服務(wù)器跪了倾剿,心里臥槽一句之后,就趕緊通知客戶端蚌成。這個(gè)過程前痘,客戶端這個(gè)傻白甜還是使勁發(fā)請求。

它收到注冊中心的通知之后担忧,就懵逼了芹缔,心里一萬句MMP飄過之后,終于接受了自己剛才發(fā)出去的一些請求瓶盛,收不到響應(yīng)了的事實(shí)最欠。

如果要是客戶端咔嚓一下關(guān)機(jī)呢?

所以我們可以看到惩猫,不論是客戶端突然關(guān)機(jī)還是服務(wù)端突然關(guān)機(jī)芝硬,都會(huì)造成問題。

于是我們的優(yōu)雅關(guān)機(jī)轧房,就是要解決這么一個(gè)問題拌阴。從前面的那些圖可以看到,如果要優(yōu)雅退出锯厢,關(guān)鍵在于好好商量:

如果客戶端關(guān)機(jī)呢皮官?那就更加簡單了脯倒,稍微停一下实辑,把發(fā)出去的請求的響應(yīng)收完再關(guān)機(jī)。

現(xiàn)實(shí)的情況是藻丢,一個(gè)節(jié)點(diǎn)剪撬,往往既是服務(wù)端,也是客戶端悠反。這種情況下該怎么搞残黑?首先在發(fā)出關(guān)機(jī)的信號(hào)后馍佑,它肯定不能關(guān)掉,至少要等到已接受的請求處理完成梨水,才能關(guān)掉拭荤。往往是,處理一個(gè)請求會(huì)導(dǎo)致它作為客戶端發(fā)起一個(gè)調(diào)用疫诽。于是我們可以看到舅世,在該節(jié)點(diǎn)既是服務(wù)端,又是客戶端的情況下奇徒,要先關(guān)閉作為服務(wù)端的功能雏亚,這樣才能防止因?yàn)橐幚硇碌恼埱蠖坏貌蛔鳛榭蛻舳讼騽e的服務(wù)器發(fā)起請求。

所以最終步驟就是:

  1. 告知注冊中心摩钙,即將關(guān)閉罢低,此時(shí)等待并處理請求;
  2. 注冊中心通知?jiǎng)e的客戶端胖笛,別的客戶端停止發(fā)送新請求网持,等待已發(fā)請求的響應(yīng);
  3. 節(jié)點(diǎn)處理完所有接收到的請求并且返回響應(yīng)后匀钧,釋放作為服務(wù)端相關(guān)的組件和資源翎碑;
  4. 節(jié)點(diǎn)釋放作為客戶端的組件和資源;

實(shí)現(xiàn)

如何知道關(guān)機(jī)之斯?

不管我們?nèi)绾螌?shí)現(xiàn)優(yōu)雅關(guān)機(jī)日杈,第一個(gè)要解決的就是,我怎么知道這個(gè)節(jié)點(diǎn)要關(guān)機(jī)了佑刷?在Java虛擬機(jī)里面莉擒,有Runtime提供了addShutdownHook的方法:

golang就沒這個(gè)便利。好在golang提供了信號(hào)(Signal)機(jī)制瘫絮。在golang里有一個(gè)os/signal的包涨冀,它是一個(gè)對操作系統(tǒng)信號(hào)的封裝——所以這是一個(gè)操作系統(tǒng)相關(guān)的東西,不過我這里只考慮Unix-Like系統(tǒng)麦萤,畢竟我還是不怎么聽說有人在Windows上部署golang微服務(wù)的[手動(dòng)狗頭]鹿鳖。

golang的文檔(https://golang.org/pkg/os/signal/)里面有很詳細(xì)的描述。我大概總結(jié)一下:

  1. SIGKILLSIGSTOP可能捕捉不到壮莹;
  2. SIGHUP翅帜,SIGINTSIGTERM會(huì)導(dǎo)致系統(tǒng)退出;
  3. SIGQUIT命满,SIGILL涝滴,SIGTRAPSIGABRTSIGSTKFLT歼疮,SIGEMT杂抽,SIGSYS會(huì)導(dǎo)致系統(tǒng)退出,并且打印此時(shí)的棧韩脏;

所以我們只需要監(jiān)聽這些信號(hào)的處理就可以了缩麸。

釋放資源步驟

前面我們討論了關(guān)機(jī)釋放資源所需要按序執(zhí)行的步驟,那么落地到dubbogo里面該如何實(shí)現(xiàn)呢赡矢?

dubbogo的源碼能夠發(fā)現(xiàn)匙睹,關(guān)鍵的組件就是RegistryProtocol

其中Protocol從邏輯上來說济竹,可以分成供Provider使用的Protocol和供Consumer使用的Protocol痕檬。當(dāng)然,Protocol也可能同時(shí)提供兩者使用送浊。因此我們考慮到這種情況梦谜,在銷毀ProviderProtocol的時(shí)候,要把共用的那些Protocol剔除出來袭景。

按照我們的預(yù)先分析的步驟唁桩,釋放資源的步驟應(yīng)該是:

  1. 銷毀所有的Registry實(shí)例,這也就是從注冊中心里面注銷耸棒。這個(gè)過程荒澡,客戶端因?yàn)橛斜O(jiān)聽注冊中心的事件,所以很快就能知道某個(gè)服務(wù)器已經(jīng)不可用与殃;
  1. 在步驟1之后单山,理論上來說所有的客戶端都不會(huì)再發(fā)請求過來了。但是還有很多時(shí)候幅疼,一個(gè)是注冊中心通知客戶端的延時(shí)米奸,二是不同的客戶端可能有一些奇怪的緩存機(jī)制,再一個(gè)就是此時(shí)正在發(fā)送的請求爽篷。這幾種情況下悴晰,還會(huì)有部分請求到達(dá)服務(wù)器,所以服務(wù)器還需要接收這部分請求然后處理掉逐工,因而要等待一段時(shí)間铡溪;
  1. 在步驟2之后,絕大部分情況下泪喊,服務(wù)端就可以直接銷毀掉扮演ProviderProtocol了棕硫。然而,如果步驟2等待時(shí)間過短窘俺,或者說客戶端和注冊中心就服務(wù)器下線這個(gè)事情達(dá)成一致的時(shí)間太長饲帅,那么這個(gè)階段還會(huì)收到請求。這個(gè)時(shí)候我們就只能拒絕請求了瘤泪。此時(shí)灶泵,我們還要判斷一下,當(dāng)前正在處理的請求處理完了沒有对途,如果處理完了赦邻,或者等了一段時(shí)間之后都還沒處理完,就進(jìn)入下一個(gè)階段实檀;
  1. 在這個(gè)步驟惶洲,服務(wù)器才真的摧毀作為ProviderProtocol

  2. 經(jīng)過步驟4膳犹,服務(wù)器還可能處在一種“雖然我無法響應(yīng)別人恬吕,但是我還在處理點(diǎn)事情,我要等別人的響應(yīng)”的狀態(tài)中须床,所以這個(gè)時(shí)候我們再稍微停下來等一下铐料,如果所有的響應(yīng)都收到請求了,或者超時(shí)豺旬,進(jìn)入下一個(gè)階段钠惩;

  1. 摧毀掉剩下的Protocol
  2. 理論上來說族阅,經(jīng)過步驟6篓跛,在框架層面上,所有的資源都釋放了坦刀。但是這個(gè)時(shí)候我們要考慮到開發(fā)者可能在此時(shí)需要釋放他創(chuàng)建的資源愧沟,因此我們要提供一個(gè)回調(diào)機(jī)制,允許他們在這個(gè)時(shí)間節(jié)點(diǎn)回收資源鲤遥;

我們的源碼里面很容易看出來這些步驟:

如何確定每一步的超時(shí)時(shí)間

在實(shí)現(xiàn)這個(gè)優(yōu)雅退出的時(shí)候央渣,有一個(gè)參數(shù)非常關(guān)鍵,就是每一步的退出時(shí)間step_timeout渴频,它代表的是芽丹,在前面提及的每一個(gè)步驟,如果需要停下來等待卜朗,那么會(huì)在多久以后超時(shí)拔第,結(jié)束等待。

在大大大大大多數(shù)情況下场钉,設(shè)置這個(gè)時(shí)間只需要考慮第一個(gè)停下來等待的步驟蚊俺,即服務(wù)端在宣稱了自己要停機(jī),并且銷毀了Registry之后停下來等待新請求的時(shí)長逛万。也就是執(zhí)行方法waitAndAcceptNewRequests的超時(shí)時(shí)間泳猬。

有一個(gè)簡單的式子可以描述這個(gè)時(shí)間:客戶端收到注冊中心通知的時(shí)長+請求響應(yīng)時(shí)長。

第一個(gè)“客戶端收到注冊中心通知的時(shí)長”很好理解,但是也比較難估算得封。這主要取決于注冊中心和客戶端緩存機(jī)制埋心。我個(gè)人經(jīng)驗(yàn)是使用ZK的情況下,一般不會(huì)超過1秒忙上。

第二個(gè)“請求響應(yīng)時(shí)長”最復(fù)雜了拷呆。首先,這是一個(gè)從客戶端觀察的值疫粥。也就是說茬斧,它不是我們監(jiān)控到的服務(wù)端的服務(wù)響應(yīng)時(shí)間,而是從客戶端發(fā)出一個(gè)請求到它收到完全響應(yīng)的時(shí)長梗逮。于服務(wù)端而言项秉,大概是“請求傳輸時(shí)長+服務(wù)響應(yīng)時(shí)長+響應(yīng)傳輸時(shí)長”。

然后我們又會(huì)面臨一個(gè)問題慷彤,一個(gè)服務(wù)端往往提供多個(gè)服務(wù)伙狐,我該取哪個(gè)服務(wù)的請求響應(yīng)時(shí)長?答案是取決于你具體的業(yè)務(wù)和你的期望瞬欧。開發(fā)者可以基于自己的服務(wù)的重要性贷屎,取比較重要的服務(wù)的999線;又或者全部服務(wù)一起考慮艘虎,取999線唉侄。這里我比較不建議使用平均線,因?yàn)槠骄€意味著有很多的請求無法再這個(gè)時(shí)間內(nèi)返回響應(yīng)野建。

另外一種比較罕見的選擇是属划,使用定時(shí)任務(wù)的執(zhí)行時(shí)間,或者事務(wù)——尤其是分布式事務(wù)——的完成時(shí)間候生。

核心就是同眯,你覺得哪個(gè)東西最重要,你就用那個(gè)東西的執(zhí)行時(shí)間唯鸭。

上面的邏輯也適用于單純是Consumer的應(yīng)用须蜗。

大多數(shù)情況下,step_timeout默認(rèn)值10秒足以應(yīng)付了目溉。

未實(shí)現(xiàn)部分

特殊回調(diào)

這個(gè)小標(biāo)題有點(diǎn)不太準(zhǔn)確明肮。大家注意到的是,我只在所有框架資源都被銷毀之后才會(huì)回調(diào)開發(fā)者注冊的回調(diào)缭付。這個(gè)時(shí)候就有這么一些問題:

  1. 如果開發(fā)者在自定義的回調(diào)里面希望用到dubbogo的功能柿估,特別是發(fā)起遠(yuǎn)程調(diào)用,那么顯然是不可能的——雖然我也覺得不會(huì)有人會(huì)這么干陷猫;
  2. 如果開發(fā)者的回調(diào)希望按照順序來執(zhí)行秫舌,那么也是不支持的的妖。我們只會(huì)按照注冊回調(diào)的順序來依次調(diào)用。當(dāng)然開發(fā)者可以通過將多個(gè)回調(diào)按序調(diào)用組成一個(gè)更復(fù)雜的回調(diào)來實(shí)現(xiàn)這個(gè)目標(biāo)足陨。不支持它主要是一個(gè)取舍問題嫂粟。我相信有這種需求的人是少數(shù)以至于幾乎沒有的……

底層支持的優(yōu)雅停機(jī)

前面所有的步驟,都是直接建立在應(yīng)用層面上钠右。實(shí)際上,還有一些業(yè)界的做法忘蟹,是在底層協(xié)議上就直接提供了支持飒房。比如說,通過TCP連接發(fā)送一個(gè)只讀事件媚值,那么客戶端后續(xù)就自然不會(huì)再把請求發(fā)過來狠毯。

我們的優(yōu)雅停機(jī)并沒有使用到這一種機(jī)制,因?yàn)樵趹?yīng)用層面上就能夠解決褥芒。dubbogo里面的RegistryProtocolDestroy都沒采用這種機(jī)制嚼松。

但是這的確是一個(gè)很不錯(cuò)的實(shí)現(xiàn)思路。dubbo就是采用了這種方式锰扶。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末献酗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子坷牛,更是在濱河造成了極大的恐慌罕偎,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件京闰,死亡現(xiàn)場離奇詭異颜及,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蹂楣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門俏站,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人痊土,你說我怎么就攤上這事肄扎。” “怎么了赁酝?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵反浓,是天一觀的道長。 經(jīng)常有香客問我赞哗,道長雷则,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任肪笋,我火速辦了婚禮月劈,結(jié)果婚禮上度迂,老公的妹妹穿的比我還像新娘。我一直安慰自己猜揪,他們只是感情好惭墓,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著而姐,像睡著了一般腊凶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拴念,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天钧萍,我揣著相機(jī)與錄音,去河邊找鬼政鼠。 笑死风瘦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的公般。 我是一名探鬼主播万搔,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼官帘!你這毒婦竟也來了瞬雹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對情侶失蹤刽虹,失蹤者是張志新(化名)和其女友劉穎挖炬,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體状婶,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡意敛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了膛虫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片草姻。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖稍刀,靈堂內(nèi)的尸體忽然破棺而出撩独,到底是詐尸還是另有隱情,我是刑警寧澤账月,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布综膀,位于F島的核電站,受9級(jí)特大地震影響局齿,放射性物質(zhì)發(fā)生泄漏剧劝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一抓歼、第九天 我趴在偏房一處隱蔽的房頂上張望讥此。 院中可真熱鬧拢锹,春花似錦、人聲如沸萄喳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽他巨。三九已至充坑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間染突,已是汗流浹背捻爷。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留觉痛,地道東北人役衡。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓茵休,卻偏偏與公主長得像薪棒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子榕莺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容