摘要:常見的降級(jí)方案表現(xiàn)形式無非以下三種類型。的級(jí)別最低最先可以被降級(jí)掉。一旦當(dāng)系統(tǒng)壓力過大的時(shí)候,先把級(jí)別的功能降級(jí)掉。降級(jí)實(shí)現(xiàn)首先要制定觸發(fā)機(jī)制。將耗時(shí)的數(shù)據(jù)落盤操作降級(jí)為異步進(jìn)行。
如果這是第二次看到我的文章,歡迎掃描文末二維碼訂閱我喲~
本文長度為4069字,建議閱讀11分鐘。
也許你對(duì)降級(jí)已經(jīng)有了一些認(rèn)識(shí),認(rèn)真看完,我想這篇文章可能會(huì)給你帶來一些新的收獲~
前面兩篇我們已經(jīng)聊過了「熔斷」(如何在到處是“雷”的系統(tǒng)中「明哲保身」?這是第一招)和「限流」(想通關(guān)「限流」?只要這一篇),這次我們聊的就是「高可用三劍客」中剩下的「降級(jí)」。
不知道這里有多少小伙伴接觸過阿里的開放平臺(tái)。在每次大促的時(shí)候,阿里都會(huì)發(fā)布這樣的一個(gè)公告。
這些調(diào)整就是「降級(jí)」工作,目的是為了騰出更多資源給核心程序使用,以最大化保證核心業(yè)務(wù)的可用性,因此就必然需要對(duì)非核心業(yè)務(wù)執(zhí)行一些降級(jí)處理。
一、什么是「降級(jí)」降級(jí)的目的用一句話概括就是:將有限的資源效益最大化。
什么樣才是效益最大化呢?就像下面這個(gè)例子:
z哥有3個(gè)東西要買,一個(gè)3000的A、一個(gè)700的B、一個(gè)1200的C,對(duì)z哥的重要程度A>B>C。但此時(shí),z哥手里只有3000塊錢,你說z哥該怎么選才能把錢花的最多?必然是選A咯。
根據(jù)28原則,我們知道一個(gè)系統(tǒng)80%的效益是由最核心的20%的功能產(chǎn)出的。剩下的20%效益需要投入80%的資源才能達(dá)到。
這就意味著,假如系統(tǒng)平時(shí)需要花費(fèi)100%資源做100%的事情,如果現(xiàn)在訪問量增多3倍的話必定扛不?。ㄐ枰?00%的資源)。那么,在不增加資源的情況下,我希望系統(tǒng)不能宕機(jī),依舊能正常工作,必然需要讓出那解決剩下20%問題的80%資源。如此一來,理論上這100%的資源就可以支撐原先5倍的訪問量。副作用是功能的完整性上受損80%。
當(dāng)然,在實(shí)際的場景中不會(huì)降級(jí)掉80%的功能這么夸張,畢竟還得為用戶的體驗(yàn)考慮。
舉個(gè)電商場景典型的例子,在大促的時(shí)候,最重要的是什么?轉(zhuǎn)化咯~賺錢咯~ 那么這個(gè)時(shí)候如果說「評(píng)論」功能占用了很多資源,你會(huì)怎么處理?其實(shí)我們可以選擇臨時(shí)關(guān)閉提交評(píng)論入口、關(guān)閉翻頁功能等等,讓下單的過程有更多的資源來處理。
常見的降級(jí)方案表現(xiàn)形式無非以下三種類型。
為了減少對(duì)「冷數(shù)據(jù)」的獲取,禁用列表的翻頁功能。
為了放緩流量進(jìn)入的速率,增加驗(yàn)證碼機(jī)制。
為了減少“大查詢”浪費(fèi)過多的資源,提高篩選條件要求(禁用模糊查詢、部分條件必選等)。
用通用的靜態(tài)化數(shù)據(jù)代替「千人千面」的動(dòng)態(tài)數(shù)據(jù)。
甚至更簡單粗暴的,直接掛一個(gè)頁面顯示「XX功能在XX時(shí)間內(nèi)暫時(shí)關(guān)閉」。
此類方案雖然或多或少降低了用戶的體驗(yàn),但是在某些時(shí)期,有些功能并不是「剛需」。以此換取對(duì)系統(tǒng)的保護(hù)是筆劃算的買賣。
還有一些功能是「防御性」的,如果愿意冒險(xiǎn)“裸奔”一段時(shí)間也會(huì)帶來可觀的資源節(jié)約。
比如通過臨時(shí)關(guān)閉「風(fēng)控」、取消部分「條件是否滿足」的判斷(如,將積分商品添加到購物車時(shí)判斷積分夠不夠)等操作,減少這類「驗(yàn)證」動(dòng)作以釋放更多的資源。
又或者將原本info、warning級(jí)別的日志采集關(guān)閉或者直接不采集,僅采集error以及fault級(jí)別的日志。
一個(gè)事件發(fā)生后立馬看到效果是一個(gè)很符合「思維慣性」的東西。但是根據(jù)之前的一篇文章(分布式系統(tǒng)關(guān)注點(diǎn)——數(shù)據(jù)一致性(上篇))我們知道,時(shí)效性這個(gè)東西一旦涉及到網(wǎng)絡(luò)傳輸是不存在真正的“實(shí)時(shí)”的。但是為了盡可能快的將處理后的結(jié)果反映到相關(guān)的地方,你會(huì)做很多努力。比如庫存的及時(shí)同步。
如果在特殊時(shí)期,能夠臨時(shí)降低對(duì)時(shí)效性的要求(3秒內(nèi)生效變成30秒生效),也是一個(gè)有不錯(cuò)收益的方案。
比如原先在商品頁會(huì)顯示當(dāng)前還剩多少個(gè)庫存,現(xiàn)在可以調(diào)整成固定顯示「有貨」。
以及將一些原本就是異步進(jìn)行的操作,處理效率放緩,甚至?xí)壕徱欢螘r(shí)間。如,送積分、送券等等。
講了這么多,降級(jí)具體實(shí)施起來要怎么做呢?
二、「降級(jí)」怎么做主要分為兩個(gè)環(huán)節(jié):定級(jí)定序和降級(jí)實(shí)現(xiàn)。
定級(jí)定序就像前面的例子中提到的一樣,首先我們得先確定每個(gè)功能的「重要程度」,它決定了在什么情況下可以拋棄它以保證剩下的功能可用。
類似于給日志定義級(jí)別一樣,比如我們可以定義1~5五個(gè)級(jí)別,1的級(jí)別最高,要拼死保護(hù)。5的級(jí)別最低最先可以被降級(jí)掉。
一旦當(dāng)系統(tǒng)壓力過大的時(shí)候,先把級(jí)別5的功能降級(jí)掉。如果還不夠再降級(jí)別4、級(jí)別3,以此類推。
但實(shí)際上光這樣定級(jí)還不夠,比如被定義為4級(jí)的有100個(gè)功能,需要降級(jí)的時(shí)候是一起降級(jí)嗎?很明顯粒度太粗了。
如果「定級(jí)」好比是橫著切蛋糕的話,「定序」就是再來豎著切。
我們也可以來定義一些數(shù)字,比如序號(hào)1~9,序號(hào)9最先被降級(jí)。
然后,你可以以每個(gè)程序所支撐的上游程序/功能數(shù)量作為一個(gè)參考標(biāo)準(zhǔn)。比如,同樣是級(jí)別5的程序,一個(gè)支撐了上游5個(gè)功能,一個(gè)支撐了10個(gè)功能,很顯然前者的序號(hào)應(yīng)該更大,更先被降級(jí)。
當(dāng)然,根據(jù)所支撐的功能數(shù)量只是一個(gè)「業(yè)務(wù)無關(guān)性」的通用辦法。如果想精益求精,還需要對(duì)每個(gè)功能做「作用」上的分析,畢竟不同功能之間的相對(duì)重要性還是有所差異的。(這里可以擴(kuò)展了解一下Analytic Hierarchy Process,層次分析法,簡稱AHP)
對(duì)了,定級(jí)定序的時(shí)候有一點(diǎn)是需要格外注意的:某個(gè)程序所依賴的下游程序的級(jí)別不能低于該程序的級(jí)別。
為什么呢?因?yàn)橐坏┧蕾嚨某绦虮唤导?jí)了,自然會(huì)導(dǎo)致其所支撐的所有上游程序不可用。所以,其上游程序的等級(jí)再高也是沒有意義的。
至此,完成了“排兵布陣”,接下來就是“實(shí)施運(yùn)作”了。
首先要制定觸發(fā)機(jī)制。這同熔斷、限流一樣,什么時(shí)候該觸發(fā)「降級(jí)」這個(gè)動(dòng)作也需要依賴提前制定的一些策略。這部分內(nèi)容和前面兩篇(熔斷、限流)類似,無非是接口的超時(shí)率、錯(cuò)誤率,或者系統(tǒng)的資源耗用率等,這里就不重復(fù)展開了。
當(dāng)程序發(fā)現(xiàn)滿足了降級(jí)條件進(jìn)入「降級(jí)模式」后,程序該如何處理請(qǐng)求呢?
全局變量 int _runLevel = 3; //運(yùn)行系統(tǒng)級(jí)別,默認(rèn)值5 全部變量 int _runIndex = 7; //運(yùn)行系統(tǒng)序號(hào),默認(rèn)值9 //以下是一個(gè)level=4、index=8的功能示例。 if(myLevel > _runLevel and myIndex > _runIndex){ // 進(jìn)入降級(jí)模式。 }else{ // do something... }
題外話:通過Aop+注解(特性)的方式來做上面的if判斷是一個(gè)爽的事情。
雖然處理請(qǐng)求的方式有很多,但特別強(qiáng)調(diào)的是,要實(shí)現(xiàn)的降級(jí)策略要盡可能的簡單。因?yàn)椤高呺H效應(yīng)」的存在,為了應(yīng)對(duì)突發(fā)狀況把事情反而搞復(fù)雜了就得不償失了。
那么在實(shí)現(xiàn)部分,如果是前端。我們比較常見的是:
在返回的http報(bào)文中通過Cache-Control的設(shè)置,讓后續(xù)的請(qǐng)求直接走瀏覽器緩存。
頁面中原本需要異步加載的數(shù)據(jù),直接不加載。
禁用部分操作按鈕,甚至直接告知“臨時(shí)關(guān)閉”。
動(dòng)態(tài)頁面的url通過反響代理切換到靜態(tài)頁面返回。
這里面除了禁用按鈕外,大部分事情都可以在接入層,如nginx中處理掉,這樣可以避免對(duì)業(yè)務(wù)項(xiàng)目的代碼侵入。
如果是后端程序的話,針對(duì)「讀」類型的操作,可以將“// 進(jìn)入降級(jí)模式”部分代碼寫成下面的樣子:
如果是無返回值方法。默認(rèn)return或者throw一個(gè)異常。
如果是有返回值方法。默認(rèn)返回本地mock的數(shù)據(jù)或者throw一個(gè)異常。
后端部分如果有使用一些中間件的話,直接在中間件(rpc、mq代理等)中處理掉是極好的(一般會(huì)內(nèi)置一個(gè)fallback接口待實(shí)現(xiàn)),如此也可以避免對(duì)業(yè)務(wù)代碼的侵入。
最后我們來聊聊后端程序的「寫」問題。
緩存是大型系統(tǒng)中的???,隨著系統(tǒng)規(guī)模越大,為了在性能和成本上尋求更優(yōu),不可避免的會(huì)增加復(fù)雜度引入多級(jí)緩存。如此就會(huì)變成:本地緩存 --> 分布式緩存 --> DB/源服務(wù),這樣的一個(gè)層層遞進(jìn)的關(guān)系。
平時(shí)的代碼可能是這樣的:
if(write數(shù)據(jù)庫(data) == true){ if(write分布式緩存(data) == true){ write本地緩存(data); return success; } else{ rollback數(shù)據(jù)庫(data); return fail; } } else{ return fail; }
在高負(fù)載時(shí)期,我們可以降低對(duì)一致性的要求。將耗時(shí)的「數(shù)據(jù)落盤」操作降級(jí)為「異步」進(jìn)行。
if(write分布式緩存(data) == true){ write本地緩存(data); pushMessage(data); //發(fā)出的消息可以通過集中式的MQ、也可以直接寫本地磁盤。 return success; } else{ return fail; }
甚至,如果可以的話能做的更徹底,同步到分布式緩存也異步進(jìn)行。
write本地緩存(data); pushMessage(data); //發(fā)出的消息可以通過集中式的MQ、也可以直接寫本地磁盤。 return success;
數(shù)據(jù)庫是系統(tǒng)的最后一座堡壘,非非非常極端的情況下,我們可以把一些「寫數(shù)據(jù)」操作在「數(shù)據(jù)庫訪問框架」中給禁用了,讓給所有資源都給到「讀數(shù)據(jù)」。使得系統(tǒng)從表象上來看至少還是“活著站在那”的,雖然很多功能操作一下就是返回失?。ㄟ@不也是實(shí)在沒辦法了嘛,面子得要啊,死撐~)。
至此我們聊了做降級(jí)的思路以及最常見的一些實(shí)現(xiàn)方式,但是真正要把降級(jí)最好是一個(gè)任重而道遠(yuǎn)的過程。
從方案的角度來說,如果降級(jí)的過程需對(duì)每個(gè)功能/程序逐一進(jìn)行,那么理論上10個(gè)功能點(diǎn)就可以產(chǎn)生P(10,10)= 3628800種方案。
再從現(xiàn)實(shí)的角度來說,流量又是不可預(yù)測的。某些功能可能這次需要作為level2來看待,下次其實(shí)作為level3就夠了。
所以這是一個(gè)需要長期不斷打磨和調(diào)優(yōu)的過程。
最后,希望近期的「高可用三劍客」可以作為你了解「高可用」的起點(diǎn),可以先收藏防身(當(dāng)然再分享一下也是極好的:)),歡迎后續(xù)一起交流探討~
Question:
你曾經(jīng)是否有遇到過什么場景,當(dāng)時(shí)是通過馬上改代碼來「降級(jí)」呢?歡迎來吐槽~
相關(guān)文章:
如何在到處是“雷”的系統(tǒng)中「明哲保身」?這是第一招
想通關(guān)「限流」?只要這一篇
分布式系統(tǒng)關(guān)注點(diǎn)——數(shù)據(jù)一致性(上篇)
作者:Zachary
出處:https://www.cnblogs.com/Zacha...
?關(guān)于作者:張帆(Zachary,個(gè)人微信號(hào):Zachary-ZF)。堅(jiān)持用心打磨每一篇高質(zhì)量原創(chuàng)。歡迎掃描下方的二維碼加入哦~。
定期發(fā)表原創(chuàng)內(nèi)容:架構(gòu)設(shè)計(jì)丨分布式系統(tǒng)丨產(chǎn)品丨運(yùn)營丨一些思考。如果你是初級(jí)程序員,想提升但不知道如何下手。又或者做程序員多年,陷入了一些瓶頸想拓寬一下視野。歡迎關(guān)注我的公眾號(hào)「跨界架構(gòu)師」,回復(fù)「技術(shù)」,送你一份我長期收集和整理的思維導(dǎo)圖。
如果你是運(yùn)營,面對(duì)不斷變化的市場束手無策。又或者想了解主流的運(yùn)營策略,以豐富自己的“倉庫”。歡迎關(guān)注我的公眾號(hào)「跨界架構(gòu)師」,回復(fù)「運(yùn)營」,送你一份我長期收集和整理的思維導(dǎo)圖。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/72759.html
摘要:如果你對(duì)異步的了解比較模糊的話,這次可以帶你一次性深入淺出。同步異步任何事物都是有利有弊的。這也導(dǎo)致了在異步環(huán)境下做事務(wù)的成本更高。但是,異步在跨進(jìn)程通訊中更合適抽象成事件來進(jìn)行協(xié)作。 如果第二次看到我的文章,歡迎「文末」掃碼訂閱我個(gè)人的公眾號(hào)(跨界架構(gòu)師)喲~?每周五早8點(diǎn) 按時(shí)送達(dá)到公眾號(hào)。當(dāng)然了,也會(huì)時(shí)不時(shí)加個(gè)餐~ Z哥在前面的三篇文章里和你一起聊了「高性能」主題下與「緩存」...
摘要:經(jīng)過一系列優(yōu)化后,在平臺(tái)上,點(diǎn)擊到頁面首屏展示的耗時(shí)從平均多降低為,優(yōu)化以上。而現(xiàn)在頁面為了更好地為用戶推薦喜歡的內(nèi)容,我們后臺(tái)引入機(jī)器學(xué)習(xí)和隨機(jī)算法來做智能個(gè)性化推薦。另外還有部分的內(nèi)容是隨機(jī)算法推薦的。 VasSonic成長歷程 前言 2017.8.8 14時(shí),SNG增值產(chǎn)品部Vas團(tuán)隊(duì)研發(fā)的輕量級(jí)高性能Hybrid框架VasSonic通過了公司最終審核,作為騰訊開源組件分享給大...
摘要:能不能只修改其中的一部分代碼并且進(jìn)行熱更新呢微內(nèi)核架構(gòu)插件架構(gòu)就適合來解決這個(gè)問題。微內(nèi)核架構(gòu)顧名思義,微內(nèi)核架構(gòu)的關(guān)鍵是內(nèi)核。微內(nèi)核架構(gòu)整體上由兩部分組成核心系統(tǒng)和插件模塊。微內(nèi)核架構(gòu)它的優(yōu)點(diǎn)是為遞進(jìn)設(shè)計(jì)和增量開發(fā)提供了方便。 如果第二次看到我的文章,歡迎下方掃碼訂閱我的個(gè)人公眾號(hào)(跨界架構(gòu)師)喲~本文長度為3633字,建議閱讀10分鐘。堅(jiān)持原創(chuàng),每一篇都是用心之作~ 如果我們的...
閱讀 3313·2021-10-08 10:04
閱讀 1273·2021-09-30 09:48
閱讀 3655·2021-09-22 10:53
閱讀 1874·2021-09-10 11:22
閱讀 1875·2021-09-06 15:00
閱讀 2332·2019-08-30 15:56
閱讀 851·2019-08-30 15:53
閱讀 2444·2019-08-30 13:04