阿里是如何抗住雙11的?看完這篇你就明白了!
我從業(yè)之初便開始扮演“救火隊員”角色,經(jīng)常去線上執(zhí)行“救火”、止損、攻關(guān)等應(yīng)急工作,再通過分析、推理、驗證…
“抽絲剝繭”的找出背后的根本原因,仿佛自己是個“經(jīng)驗豐富、從容冷靜、思維縝密”的偵探。
以前我一直認(rèn)為線上問題定位、分析處理能力是架構(gòu)師的“看家功底”并常引以為傲。
但最近這兩年我開始思考,其實“防火”比“救火”更重要,正如一句古話“上醫(yī)治未病,中醫(yī)治欲病,下醫(yī)治已病”。下面我將為大家分享穩(wěn)定性治理經(jīng)驗和阿里的穩(wěn)定性保障體系。
閱讀本文,你將會收獲:
- 高并發(fā)、大流量場景的常見問題和應(yīng)對手段
 - 知名互聯(lián)網(wǎng)公司的高可用架構(gòu)和穩(wěn)定性保障體系
 
穩(wěn)定性治理的常見場景
突發(fā)大流量
相信大家對上圖并不陌生,尤其在剛剛過去的雙 11、雙 12 中。這是電商大促場景中執(zhí)行了最常用的自動預(yù)案 - “限流保護(hù)”,并非很多朋友說的“宕機(jī)”、“崩潰”。
“限流”是應(yīng)對高并發(fā)或者突發(fā)大流量場景下的“三板斧”之一,不管是在電商大促、社交媒體突發(fā)熱點事件(例如:遇到“知名女星出軌”),還是在常態(tài)下都是非常有必要的保護(hù)手段。
本質(zhì)上就是檢查到當(dāng)前請求量即將超出自身處理能力時,自動執(zhí)行拒絕(或者執(zhí)行“請求排隊”),從而防止系統(tǒng)被徹底壓垮。
不穩(wěn)定服務(wù)
講到“限流”,那就不得不提另外一板斧“降級”。除了我們之前所提到的 “開關(guān)降級”(關(guān)閉次要功能或服務(wù))、兜底、降低一致性等之外,在技術(shù)層面最常用就是“自動熔斷降級”。
“限流”是為了防止大流量壓垮系統(tǒng),而“熔斷”是為了防止不穩(wěn)定的服務(wù)引發(fā)超時或等待,從而級聯(lián)傳遞并最終導(dǎo)致整個系統(tǒng)雪崩。
如圖所示,假設(shè)服務(wù) D 此時發(fā)生了故障或者 FullGC 等,則會導(dǎo)致上游的服務(wù) G、F 中產(chǎn)生大量等待和異常,并級聯(lián)傳遞給最上游的服務(wù) A、B。
即便在服務(wù) G、F 中設(shè)置了“超時”(如果沒有設(shè)置“超時”那情況就更糟糕了),那么也會導(dǎo)致線程池中的大量線程資源被占用。
如果服務(wù) H、I 和服務(wù) G、F 在同一個應(yīng)用中且默認(rèn)共用同一個線程池,那么也會因為資源耗盡變得不可用,并最終導(dǎo)致最上游的服務(wù) A 和服務(wù) B 整體不可用,全部鏈路都將異常,線上核心系統(tǒng)發(fā)生這種事故那就是災(zāi)難。
假如我們在檢查到服務(wù) G 和服務(wù) F 中 RT 明顯變長或者異常比例增加時,能夠讓其自動關(guān)閉并快速失敗,這樣 H 和 I 將不會受影響,最上游的服務(wù) A 和服務(wù) B 還能保證“部分可用”。
舉個現(xiàn)實生活中更通俗的例子,當(dāng)你們家的電器發(fā)生短路時空氣開關(guān)會自動跳閘(保險絲會自動 “熔斷”)。
這就是通過犧牲你們家的用電而換回小區(qū)的正常供電,否則整個線路都會燒毀,后果會不堪設(shè)想。
所以,你得結(jié)合實際業(yè)務(wù)場景先找出哪些接口、服務(wù)是可以被“降級”的。
架構(gòu)單點
這個事件大概發(fā)生在 2015 年,被載入了支付寶的“史冊”,也推動了螞蟻金服整體 LDC 架構(gòu)(三地五中心的異地多活架構(gòu))的演進(jìn)。
異地多活架構(gòu):
- 突破單機(jī)房容量限制
 - 防機(jī)房單點,高可用
 
內(nèi)建質(zhì)量
根據(jù)以往的經(jīng)驗,60% 以上的故障都是由變更引起的,請牢記變更“三板斧”:
- 可回滾/可應(yīng)急
 - 可灰度
 - 可監(jiān)控
 
預(yù)防質(zhì)量事故的常用手段:
- 做好分析、設(shè)計、評審,容量評估,規(guī)避風(fēng)險
 - 制定規(guī)范,控制流程,加入代碼掃描和檢查等
 - 閹割做好 Code Review
 - 測試用例覆蓋(通過率、行覆蓋率、分支覆蓋率),變更全量回歸
 - 盡可能的自動化,避免人肉(易出錯),關(guān)鍵時刻執(zhí)行 Double Check
 
高可用架構(gòu)的基石:穩(wěn)定性保障體系
從故障視角來看穩(wěn)定性保障,如下圖:
穩(wěn)定性保障的核心目標(biāo)如下:
- 盡早的預(yù)防故障,降低故障發(fā)生幾率。
 - 及時預(yù)知故障,發(fā)現(xiàn)定義故障。
 - 故障將要發(fā)生時可以快速應(yīng)急。
 - 故障發(fā)生后能快速定位,及時止損,快速恢復(fù)。
 - 故障后能夠從中吸取教訓(xùn),避免重復(fù)犯錯。
 
仔細(xì)思考一下,所有的穩(wěn)定性保障手段都是圍繞這些目標(biāo)展開的。
穩(wěn)定性保障體系
上圖涵蓋了穩(wěn)定性體系的各個方面,下面我來一一講解。
應(yīng)用架構(gòu)穩(wěn)定性
應(yīng)用架構(gòu)穩(wěn)定性相對是比較廣的話題,按我的理解主要包括很多設(shè)計原則和手段:
①架構(gòu)設(shè)計簡單化。系統(tǒng)架構(gòu)簡單清晰,易于理解,同時也需要考慮到一定的擴(kuò)展性,符合軟件設(shè)計中 KISS 原則。
現(xiàn)實中存在太多的“過度設(shè)計”和“為了技術(shù)而技術(shù)”,這些都是反例,架構(gòu)師需懂得自己權(quán)衡。
②拆分。拆分是為了降低系統(tǒng)的復(fù)雜度,模塊或服務(wù)“自治”,符合軟件設(shè)計中“單一職責(zé)”原則。拆分的太粗或者太細(xì)都會有問題,這里沒有什么標(biāo)準(zhǔn)答案。
應(yīng)該按照領(lǐng)域拆分(感興趣同學(xué)可以學(xué)習(xí)下 DDD 中的限界上下文),結(jié)合業(yè)務(wù)復(fù)雜程度、團(tuán)隊規(guī)模(康威定律)等實際情況來判斷??梢韵胂?5 個人的小團(tuán)隊去維護(hù)超過 30 多個系統(tǒng),那一定是很痛苦的。
③隔離。拆分本質(zhì)上也是一種系統(tǒng)級、數(shù)據(jù)庫級的隔離。此外,在應(yīng)用內(nèi)部也可以使用線程池隔離等。分清“主、次”,找出“高風(fēng)險”的并做好隔離,可以降低發(fā)生的幾率。
④冗余。避免單點,容量冗余。機(jī)房是否單點,硬件是否單點,應(yīng)用部署是否單點,數(shù)據(jù)庫部署是否單點,鏈路是否單點…硬件和軟件都是不可靠的,冗余(“備胎”)是高可用保障的常規(guī)手段。
⑤無狀態(tài)、一致性、并發(fā)控制、可靠性、冪等性、可恢復(fù)性…等。比如:投遞了一個消息,如何保障消費端一定能夠收到?上游重試調(diào)用了你的接口,保證數(shù)據(jù)不會重復(fù)?Redis 節(jié)點掛了分布式鎖失效了怎么辦?…這些都是在架構(gòu)設(shè)計和功能設(shè)計中必須考慮的。
⑥盡可能的異步化,盡可能的降低依賴。異步化某種程度可以提升性能,降低 RT,還能減少直接依賴,是常用的手段。
⑦容錯模式。
我在團(tuán)隊中經(jīng)常強(qiáng)調(diào)學(xué)會“面向失敗和故障的設(shè)計”,盡可能做一個“悲觀主義者”,或許有些同學(xué)會不屑的認(rèn)為我是“杞人憂天”,但事實證明是非常有效的。
從業(yè)以來我有幸曾在一些高手身邊學(xué)習(xí),分享受益頗多的兩句話:
- 出來混,遲早要還的
 - 不要心存僥幸,你擔(dān)心的事情遲早要發(fā)生的
 
上圖是比較典型的互聯(lián)網(wǎng)分布式服務(wù)化架構(gòu),如果其中任意紅色的節(jié)點出現(xiàn)任何問題,確定都不會影響你們系統(tǒng)正常運行嗎?
限流降級
前面介紹過了限流和降級的一些場景,這里簡單總結(jié)下實際使用中的一些關(guān)鍵點。
以限流為例,你需要先問自己并思考一些問題:
- 你限流的實際目的是什么?僅僅只做過載保護(hù)?
 - 需要什么限流策略,是“單機(jī)限流”還是“集群限流”?
 - 需要限流保護(hù)的資源有哪些?網(wǎng)關(guān)?應(yīng)用?
 - 水位線在哪里?限流閾值配多少?
 - …
 
同理,降級你也需要考慮:
- 系統(tǒng)、接口依賴關(guān)系
 - 哪些服務(wù)、功能可以降級掉
 - 是使用手工降級(在動態(tài)配置中心里面加開關(guān))還是自動熔斷降級?熔斷的依據(jù)是什么?
 - 哪些服務(wù)可以執(zhí)行兜底降級的?怎么去兜底(例如:掛了的時候走緩存或返回默認(rèn)值)?
 - …
 
這里我先賣下關(guān)子,篇幅關(guān)系,下篇文章中我會專門講解。
監(jiān)控預(yù)警
上圖是我個人理解的一家成熟的互聯(lián)網(wǎng)公司應(yīng)該具備的監(jiān)控體系。前面我提到過云原生時代“可觀察性”,也就是監(jiān)控的三大基石。
這里再簡單補(bǔ)充一下 “健康檢查”,形成經(jīng)典的“監(jiān)控四部曲”:
關(guān)于健康檢查,主要就分為“服務(wù)端輪詢”和“客戶端主動上報”兩種模式,總的來講各有優(yōu)缺點,對于類似 MySQL 這類服務(wù)是無法主動上報的(除非借助第三方代理)。需要注意的是傳統(tǒng)基于端口的健康檢查很難識別“進(jìn)程僵死”的問題。
提到監(jiān)控那就不得不提 Google SRE 團(tuán)隊提出的監(jiān)控四項黃金指標(biāo):
- 延遲:響應(yīng)時間
 - 流量:請求量、QPS
 - 錯誤:錯誤數(shù)、錯誤率
 - 飽和度:資源利用率
 
類似的還有 USE 方法,RED 方法,感興趣的讀者可以自行查閱相關(guān)資料。
云原生時代通常是應(yīng)用/節(jié)點暴露端點,監(jiān)控服務(wù)端采用 Pull 的方式采集指標(biāo),當(dāng)前比較典型的就是 Prometheus 監(jiān)控系統(tǒng),感興趣的可以詳細(xì)了解。
當(dāng)然,以前也有些監(jiān)控是通過 Agent 采集指標(biāo)數(shù)據(jù)然后上報到服務(wù)端的。
除了監(jiān)控自身,告警能力也是非常重要的。告警的閾值配多少也需要技巧,配太高了不靈敏可能會錯過故障,配太低了容易“監(jiān)控告警疲勞”。
強(qiáng)弱依賴治理
這是網(wǎng)上找的一張“系統(tǒng)依賴拓?fù)鋱D”,可見面對這種復(fù)雜性,無論是通過個人經(jīng)驗,還是借助“鏈路跟蹤工具”都很難快速理清的。
何為強(qiáng)弱依賴?A 系統(tǒng)需要借助 B 系統(tǒng)的服務(wù)完成業(yè)務(wù)邏輯,當(dāng) B 掛掉的時候會影響 A 系統(tǒng)中主業(yè)務(wù)流程的推進(jìn),這就是“強(qiáng)依賴”。
舉個例子:下單服務(wù)依賴“生成訂單號”,這就是“強(qiáng)依賴”,“扣庫存”這也是 “強(qiáng)依賴”。
同理,當(dāng) A 依賴的 B 系統(tǒng)掛掉的時候,不會影響主流程推進(jìn),那么就是“弱依賴”。例如:購物車頁面中顯示的庫存數(shù)是非必須的。
如何梳理出這種強(qiáng)弱依賴,這個在阿里內(nèi)部是有專門的中間件可以做的,目前開源社區(qū)沒有替代品,可能就要結(jié)合鏈路跟蹤+個人的經(jīng)驗,針對系統(tǒng)級,接口級去做鏈路梳理。
我們重點關(guān)注的是 “強(qiáng)依賴”,而“弱依賴” 通常是可以執(zhí)行容災(zāi)策略的,例如:可以直接降級掉,可以返回為空或者默認(rèn)值、執(zhí)行兜底等。
總結(jié)出來關(guān)鍵有幾點:當(dāng)前業(yè)務(wù)功能/應(yīng)用/服務(wù)、依賴的服務(wù)描述、依賴類型(比如:RPC 調(diào)用),峰值調(diào)用量、鏈路的流量調(diào)用比例、強(qiáng)弱等級、掛掉的后果等。
容量規(guī)劃
容量規(guī)劃是非常重要的,無論是成本控制還是穩(wěn)定性保障都需要。否則壓根不知道需要投入多少資源以及資源投入到哪里。
需要了解系統(tǒng)極限水位在哪里,再推算出合理的“閾值”來做好“過載保護(hù)”,這也是執(zhí)行是限流、降級等預(yù)案體系的關(guān)鍵依據(jù)和基礎(chǔ)。
第一階段:主要依靠經(jīng)驗值、理論值等來預(yù)估的,或者是靠“拍腦袋”的
前幾年資本市場情況比較好,互聯(lián)網(wǎng)公司比較典型的現(xiàn)象:老板,我需要買 100 臺服務(wù)器,50 臺跑應(yīng)用,20 臺跑中間件,10 臺做數(shù)據(jù)庫…預(yù)計可以扛住日均 1000W 訪問量,每天 100W 訂單…
靠譜一點的人還能扯出 “MySQL 并發(fā)連接數(shù)在幾 core 幾 G 大概能到 xxx”、 “Redis 官方稱可以達(dá)到 10W TPS”之類的參考值,這種至少聽起來還有那么一點道理。
不靠譜的人呢?那可能就真是瞎說的一個數(shù)字,或者會說“我上家公司就用了這么多支撐的”,其實純靠拍腦袋的。
總之,這都是很不靠譜的,會造成資源分配不合理,有些浪費而有些饑荒。
第二階段:通過線下壓測手段來進(jìn)行
線下壓測通常是壓測的單接口、單節(jié)點,壓測結(jié)果可以幫助我們了解應(yīng)用程序的性能狀況、定位性能瓶頸,但是要說做整體的容量規(guī)劃,那參考價值不大。
因為實際情況往往復(fù)雜太多,網(wǎng)絡(luò)帶寬、公共資源、覆蓋場景不一致、線上多場景混合等各種因素。
根據(jù)“木桶短板”的原理,系統(tǒng)的容量往往取決于最弱的那一環(huán)節(jié)。正所謂 “差之毫厘,失之千里”。
第三階段:通過線上單機(jī)壓測來做
比較常見的手段有:線上引流、流量復(fù)制、日志回放等。其中線上引流實施起來最簡單,但需要中間件統(tǒng)一。
通過接入層或服務(wù)注冊中心(軟負(fù)載中心)調(diào)整流量權(quán)重和比例,將單機(jī)的負(fù)載打到極限。這樣就比較清楚系統(tǒng)的實際水位線在哪里了。
第四階段:全鏈路壓測,彈性擴(kuò)容
這個是阿里首創(chuàng)的,目前很多公司在效仿。屬于是業(yè)務(wù)倒逼技術(shù)創(chuàng)造出來的手段。
全鏈路壓測涉及到的內(nèi)容會比較多,關(guān)鍵的步驟包括:
- 鏈路梳理
 - 基礎(chǔ)數(shù)據(jù)準(zhǔn)備、脫敏等
 - 中間件改造(透傳影子標(biāo),軟負(fù)載/消息/緩存/分庫分表等路由,建立影子表)
 - 建立壓測模型、流量模型、流量引擎
 - 預(yù)案體系的檢查
 - 執(zhí)行壓測,緊盯監(jiān)控告警
 - 數(shù)據(jù)清理
 
對業(yè)務(wù)開發(fā)團(tuán)隊同學(xué)來講,關(guān)于鏈路梳理、流量模型的評估是其中最重要的環(huán)節(jié),全鏈路壓測就是模擬用戶真實的訪問路徑構(gòu)造請求,對生產(chǎn)環(huán)境做實際演練。
這里我以大家熟悉的購買電影票的場景為例。如下圖,整個鏈路中業(yè)務(wù)流量其實是呈“漏斗模型”的,至于每一層的比例是多少,這個第一就是參考當(dāng)前的監(jiān)控,第二就是參考?xì)v史數(shù)據(jù)去推算平均值了。
漏斗模型推演示例:
可以看出每一層(不同應(yīng)用、不同服務(wù))所需要承載的真實流量不一樣,負(fù)載也是不一樣的。
當(dāng)然,實際場景更復(fù)雜,實際情況是多場景混合并行的,A 用戶在拉排期的時候 B 用戶已經(jīng)在鎖座了…
我們要做的,就是盡可能接近真實。還有最關(guān)鍵的一點要求:不能影響線上真實業(yè)務(wù)。這就需要非常強(qiáng)的監(jiān)控告警和故障隔離能力了。
關(guān)于系統(tǒng)容量和水位標(biāo)準(zhǔn),這里給大家一個建議參考值:
- 水位標(biāo)準(zhǔn):單機(jī)房部署水位應(yīng)在 70% 以下,雙機(jī)房部署水位應(yīng)在 40% 以下
 - 單機(jī)水位:單機(jī)負(fù)載 / 單機(jī)容量
 - 集群水位:集群負(fù)載 / 集群容量
 - 理論機(jī)器數(shù):實際機(jī)器數(shù) *(集群水位 / 水位標(biāo)準(zhǔn))
 
為什么雙機(jī)房是 40%?一個機(jī)房故障了流量全都切到另外一個機(jī)房去,要確保整體不受影響,不會被壓垮。
預(yù)案體系
電影片段中 “檢查到有未知生物入侵地球,聯(lián)合國宣布啟動進(jìn)入一級戒備,馬上啟動宇宙飛船達(dá)到現(xiàn)場” ,“已經(jīng)了解清楚,并按協(xié)議執(zhí)行驅(qū)逐指令,目前已經(jīng)離開” … 這就是典型的預(yù)案體系。
觸發(fā)條件、等級、執(zhí)行動作、事后情況都非常清晰,整個過程還帶有閉環(huán)的(當(dāng)然這片段是我 YY 出來的)。
前面我講過“面向失敗的設(shè)計”,就是盡可能的考慮到各種異常場景和特殊情況。
這些零散的“知識點”,還有日常的一些復(fù)盤的經(jīng)驗都可以作為日后的預(yù)案。
當(dāng)然,前面我們講過的限流、降級等應(yīng)急手段,容錯模式也是整個預(yù)案體系中非常重要的。預(yù)案積累的越豐富,技術(shù)往往越成熟。
總的來講,預(yù)案的生命周期包括:
從大的層面又可以分為:
- 事前制定和完善預(yù)案
 - 日常演練預(yù)案
 - 事中統(tǒng)一指揮,收集數(shù)據(jù),決策并執(zhí)行預(yù)案
 - 事后總結(jié)并繼續(xù)完善和改進(jìn)預(yù)案
 
當(dāng)然,這里說明下,有些預(yù)案是達(dá)到明確的觸發(fā)條件后自動執(zhí)行的,有些是需要依賴人工決策然后再觸發(fā)執(zhí)行的。
這里我給一個簡單的 Demo 給大家參考:
線上演練
正所謂 “養(yǎng)兵千日,用兵一時”。現(xiàn)實生活中的“消防演習(xí)”就是一個好例子,否則時間久了根本不知道滅火器放在哪里,滅火器是怎么打開的?
打開后還能噴出泡沫來嗎?對應(yīng)到我們技術(shù)領(lǐng)域也是一樣的,你怎么知道你的預(yù)案都是有效的?你怎么保證 on call 值班機(jī)制沒問題?你怎么知道監(jiān)控告警真的很靈敏?
在阿里內(nèi)部不管是大促還是常態(tài),都會不定期來一些線上演練。在螞蟻內(nèi)部每年都會有“紅藍(lán)軍對抗演練”,這是一種非常好的“以戰(zhàn)養(yǎng)兵”的做法。
先看一張關(guān)于故障畫像的大圖,這里列舉了典型的一些故障場景,大家不妨思考下如何通過“故障注入”來驗證系統(tǒng)的高可用能力。
簡單總結(jié)故障演練主要場景和目的:
- 預(yù)案有效性、完整性
 - 監(jiān)控告警的準(zhǔn)確性、時效性
 - 容災(zāi)能力測試
 - 檢查故障是否會重現(xiàn)
 - 檢查 on call 機(jī)制,驗證突發(fā)情況團(tuán)隊實際戰(zhàn)斗能力
 - …
 
“混沌工程”是近年來比較流行的一種工程實踐,概念由 Netflix 提出,Google、阿里在這方面的實踐經(jīng)驗比較豐富(或者說是不謀而合,技術(shù)頂尖的公司大都很相似)。
通俗點來講就是通過不斷的給現(xiàn)有系統(tǒng)“找亂子”(進(jìn)行實驗),以便驗證和完善現(xiàn)有系統(tǒng)的高可用性、容錯性等。
引用一句雞湯就是:“殺不死我的必將使我更強(qiáng)大”,混沌工程的原則:
系統(tǒng)成熟度模型:
面向故障/失敗的設(shè)計更是一種技術(shù)文化,應(yīng)該在團(tuán)隊中大力推廣。
小結(jié)
本文我主要講解了穩(wěn)定性治理的常見手段,穩(wěn)定性保障體系。其中涉及到的知識、手段、內(nèi)容都非常多。
限于篇幅的關(guān)系,不可能每一項都特別細(xì)致。還需讀者慢慢消化,更重要的是好好思考現(xiàn)狀并努力改進(jìn),也歡迎留言討論。
作者:丁浪
簡介:目前在創(chuàng)業(yè)公司擔(dān)任高級技術(shù)架構(gòu)師。曾就職于阿里巴巴大文娛和螞蟻金服。具有豐富的穩(wěn)定性保障,全鏈路性能優(yōu)化的經(jīng)驗。架構(gòu)師社區(qū)特邀嘉賓!








































 
 
 











 
 
 
 