search
尋找貓咪~QQ 地點 桃園市桃園區 Taoyuan , Taoyuan

從淘寶到雲端,阿里高可用架構演進實戰

作者|沐劍 編輯|小智 本文主要介紹近幾年在阿里電商平台及阿里雲上的高可用設計經驗,分為兩個部分:第一部分主要包括傳統的淘寶店鋪穩定性體系的建設及相關的基礎鏈路設計、緩存和容災方案的設計及部署;第二部分主要包括目前公有雲高可用設計的主要思路、經典故障及應對措施等。

註:本文整理自阿里技術專家沐劍在 QCon 北京 2017 上的演講,由阿里技術公眾號授權轉載。

寫在前面

大家好,我今天分享的題目是《高可用實踐:從淘寶到上雲的差異》,取這個標題是因為會涉及到兩個方面內容,一方面以淘寶為例子,傳統的 IDC 的時候,我們穩定性是怎麼做的,另外在雲計算背景下,有很多創業公司是基於阿里雲這樣的公有雲基礎設施做研發,在公有雲的環境下怎麼做好我們系統的高可用。

我的花名叫沐劍,2011 年加入淘寶做評價系統,2012-2015 年在店鋪平台,負責店鋪的前台瀏覽系統和後台的 RPC 服務,以及一些性能優化、雙 11 保障的事情。到了 2015 年開始到了 TAE 團隊,開始負責雲端架構及整體高可用方案,TAE 的升級版 EWS 現在也在聚石塔上面幫大量 ISV 和創業公司解決運維部署、自動化監控和性能分析等等問題。

去年我是作為阿里商家事業部雙 11 作戰項目研發的 PM。2017 年我開始接手商家營銷團隊。在阿里五六年的經驗,其實就做了幾件事,比如連續五年參加了雙十一的核心備戰,然後像去 IOE、異地多活,全鏈路壓測、安全混合雲、容器服務等項目參與設計和實施。

首先我會從淘寶店鋪角度分享,以前在店鋪是怎麼樣做雙 11 保障的,後面是一些公有雲相關的內容。

淘寶店鋪穩定性體系建設

這是一個淘寶的店鋪系統,這套系統是一個非常典型的高併發的瀏覽系統,在前幾年的雙 11 峰值有 20 萬次的 Web 頁面請求,平均一個頁面對應了 20 次的 RPC 調用,這個時候對於整個系統的集合來說每秒的 QPS 是 400 萬次,這中間就會涉及到緩存、資料庫以及其它二方的 RPC 調用,對於這樣的系統來說,在性能、穩定性和體驗間要做一個平衡,既不能純用太多的機器死扛這個訪問量,又要保障用戶的體驗。

基礎鏈路設計

從請求鏈路來說,首先 DNS 把 CDN 的 VIP 解析出來,分佈在全球不同的區域,CDN 回源到接入層分別經過 4 層和 7 層的負載均衡,近幾年會發現 CDN 這個行業早已不僅僅局限做 CSS/JS 等靜態資源的緩存,也承擔了一些動態加速和收斂的特性,所以我們是通過 CDN 做域名收斂,收斂後會把這個流量發到統一接入層,然後到應用集群,後面再經過應用存儲、Cache 這些服務。

當我們在做穩定性的時候,會考慮性能和穩定性之間是什麼關係,很多人認為這兩者是衝突的,比如我要保障一個東西的性能非常高的時候,要犧牲掉很多別的東西,可能你要引入一個非常新的框架或者基礎設施來提升性能,但它的穩定性可能是不那麼成熟的,但是從容量規劃的角度看,只有這套系統性能足夠好,才能承擔像雙 11 那樣的大訪問量。

店鋪也是一套經歷了很多年的系統,在應用層上的優化基本上已經做到極致了,我們就轉變思路,在操作系統層能不能做一些優化,這裡藉助了一個比較好的工具 perf,在操作系統層面告訴你系統調用的開銷是集中在哪裡,從 perf 上就可以定位到有一個百分比,可以看到是比如數組分配還是 GC 產生了大量的開銷。

最初我們發現是異常帶來的開銷,就會看為什麼這個系統的異常會導致 20% 以上的 CPU 開銷,最後用 BTrace 跟了一下異常的構造函數,發現是我們依賴的開源的三方包里通過異常做控制流,每一次它處理結束的時候,就拋一個 EOFException 出來,這個就導致了非常大的開銷,我們就把開源包替換掉了。

當你依賴一些底層的東西的時候,如果對原理不太了解會給你帶來一些意料之外的事情。JVM 里是有一個常量池存儲字元串常量的地方,就是一個哈希表,如果說這個表的大小不足夠大,就會從哈希查詢變成鏈表查詢,性能就會特別低。

再談一個 warm up 的問題,當我們應用剛剛啟動的時候,還沒有把位元組碼編譯成 native code,延遲非常高,用戶就得到一個有損的服務。我們現在在內部的 JVM 做一個功能,會採集線上系統的調用,把熱點方法收集下來做分析,在應用把真實流量掛上去之前,已經預先把所有的熱點方法編譯成 native code 保證這個性能。開源界也有其他的方案,比如 Azul 的 Zing 有個 ReadyNow,IBM 的 J9 有個 AOT,也是做類似的事情。

緩存設計

談到緩存,Cache 里有一些小技巧,在做雙十一備戰時發現一個店鋪的基礎服務平時每天日常就有 100 億的調用量,當時是幾十台機器估了一下可能要成倍增長,成本是非常高的,怎麼解決這個問題,當時寫了個富客戶端,讓業務方先去查我們分散式 Cache,如果命中就直接返回來,如果不命中再走我們的服務端查。這種情況下,只要你能夠保證命中率足夠高,比如 98% 的命中率,就意味著只有 2% 是需要後端伺服器承擔剩下的請求,用非常少的伺服器去承擔非常大的流量,這是成本和性能間的權衡。

在緩存方面,我們很少會關心緩存的高可用是怎麼部署的,它是一個偏運維的內容,我把緩存的部署簡化成一個雙機房的模型,因為它在高可用里是最簡單的場景。

對於緩存來說有兩種經典部署模式,第一種叫共享集群部署,在 IDC 里我的應用是分機房部署的,Cache 集群也是分機房部署,對於應用伺服器來說,兩邊的 Cache 對他來說邏輯上是一個集群,會往 IDC 1 的 Cache 寫一半過去,往 IDC 2 也寫一半過去,這種部署的好處在於,機房間網路斷掉的時候,有一半的數據是在緩存的,保證一半的數據能夠命中,不會直接死掉,另外對成本上相對比較友好,沒有浪費任何一個 Cache 的節點,這個 Cache 本身是復用的。

但是也正如剛才說的問題,如果中間斷掉了,有一半的命中率是有損的,所以就誕生了另外的一個部署模式,就是獨立部署,不管你哪個機房掛掉,命中率是基本不變的,兩邊同時保持了 98% 的命中率,但是它是成本不友好的,兩邊要同時部署,同時承擔副本的作用,並且失效時,要同時失效另外一個 IDC 2,這樣才保證一致性。

在緩存上,我認為一切東西都是可以被緩存的,通常我們認為緩存跟實際資料庫里存在的東西可能是不一樣的,有幾毫秒的延遲或者怎麼樣,所以我們設計一個系統的時候,對一致性要求非常高的時候,會傾向於不用緩存,用資料庫扛這個流量。

但以 MySQL 為例,InnoDB 里有一個很重要的緩存 Buffer Pool,對於一個資料庫,在冷庫的情況下用一堆 SQL 去查它,和慢慢預熱完再查它的時候效果是不一樣的,這個是我們當初在做異地多活時面臨的一個問題。例如我已經有一個機房,希望建立一個新單元去承擔這個機房的流量,當我建完這個單元,把所有的應用都部署好了后,把流量切 50% 過來會怎麼樣,假設這兩個單元的機器數一樣,這個單元會掛,因為這個資料庫是冷的,緩存是空的,不能承擔之前那個單元資料庫所能承擔的 QPS。

現在業界有很多叫 API 網關或者 CDN,他們在邊緣節點也做了一層短暫的 Cache,可能只 Cache 50 或者 100 毫秒,但是當你系統受到攻擊的時候可以拯救你後端的應用系統,攻擊引發的命中率通常比較高,有這 50 毫秒的緩存,可能後端只有幾百個 QPS 過來,那個流量你是可以承受的。

在高可用里兩個非常經典的做法是限流和降級,在阿里雙 11,有一位老兵說過一句話,他說當雙 11 到來的時候,任何一個系統都可能出問題,你要做的是對你的上游限流,對你的下游限流。怎麼理解,當上流的流量超過你的能力的時候就要限流,當下游比如 DBA 告訴你資料庫壓力很大了,那就對下游限流,只要保證住這個限流,你基本不會掛,每個系統都做到這個的時候,整個系統都是可用的。當流量超出你掌控的時候,這個做法可以讓你成為這個暴風下的倖存者。

對限流降級的思考,第一限流降級考驗的是什麼問題,我認為本質上考驗的是故障自恢復能力,在平時工作中會遇到機房斷網或者停電,每半個月都會做斷網演練,不告訴你發生什麼,就把這個網切斷,看你的應用 O 不 OK,一般是在晚上兩三點,接到很多的機房報警,這個時候看你的架構設計的是否足夠可用,如果足夠可用就沒問題,不會造成什麼影響,繼續睡覺,如果設計不好,就得爬起來立即處理。

而開關降級最大的作用,比如我們發現一些線上的問題,第一反映是趕緊回滾,但是當你的系統很大的時候,特別像 Java 這種,一個系統啟動要啟動幾分鐘,你的回滾完成,20 分鐘都過去了,這個過程對用戶來說都是有損的,而開關可以在一瞬間把所有的邏輯切到老的。這個是避免回滾時間導致的問題。開關有的時候能救命,如果沒有這個開關的話,避免問題放大就只能回滾,所以開關是一個很大的價值所在。

容災設計

另外一點非常重要的是,在設計一個技術方案的時候,就會把容災的設計融入到方案里。比如在設計技術方案的時候,在最後一章單獨有一個容災設計,這個節點裡任何服務掛掉的時候,你要保持什麼樣的方式保持這個服務是可用的。

在容災設計時有幾點必須考慮,比如我引了一個新 jar 包或者調了一個新的 RPC 的服務、引入了分散式的存儲,以前沒用過也不知道它穩不穩定,第一想法是它肯定會掛,它掛了我們怎麼做,我們當時在做前台系統的非同步化的時候,因為 Redis 支持 map 的數據結構,所以我們就是用 Redis 的 hmget 從這個 map 里拿出部分的 key 減少網卡的流量,但即使這個掛掉了,我們還會走老的 Cache,只不過網卡流量會大一些,但是對用戶的服務是無損的,所以這裡要考慮如果它掛了怎麼做降級,有什麼樣的恢複流程。

另外是發布計劃,在新系統上線時就會關注這些問題,比如這次有沒有做數據遷移,比如以前我是 8 個庫不夠用了我拆到 16 個庫或者 32 個庫,中間一定是有數據遷移的,涉及到數據遷移一定要有一套對賬系統保證這個數據是新數據和老數據是對得平的,不然一定有問題,因為我們是做交易相關的,訂單、金額絕對不能出問題。

另外是你的發布順序是不是有依賴,如果出了問題的時候,誰要先回滾,這裡是取決於技術設計。另外是否要通過客服公告的方式告訴外部用戶說有 5 分鐘的不可用,如果真的有用戶打電話有疑問客服同學可以向用戶解釋。

在高可用這個領域做久了會有一種直覺,這個直覺很重要,來源於你的經驗轉換成這種直覺,但是對於一個成熟的團隊來說,需要把這種直覺轉化為產品或工具。有很多牛人他們的技能都只能叫手藝,你需要把這種手藝轉換成產品和工具。

公有雲高可用設計

2015 年我去做雲產品,這裡給大家分享下我們是怎麼樣幫客戶包括我們的系統在雲上是做高可用的。

經典故障案例

首先看兩個經典故障案例,第一個是 Gitlab 生產資料庫刪了,它恢復了很久,Snapshot 等全都沒有生效,做了五六層的備份也都沒有什麼用。這個事情說明第一我們的故障要定期演練,比如中間件在做的線上故障演練,你說你的系統可用性好,我把這個主庫斷了,虛擬機掛掉幾台試試,做這些演練就可以知道你這個容災體系是不是可靠的,如果沒有這個演練的話,當真正的故障發生時你才會發現這個東西是不 OK 的。

另外一個很典型的問題,Gitlab 對備份的原理是不夠了解的。比如當時用的 PostgreSQL 的一個版本,當時是有問題的,沒有驗證,開發人員對這個又不是特別了解的情況下就會出現這個問題,這就是為什麼要去了解你的依賴以及你依賴的依賴。

去年我們做壓測,有個應用一邊壓測一邊在優化做發布,發現第一批發的起不來了,就只是改了一兩行代碼加日誌,他就去看什麼原因,最後發現依賴的某個 jar 包依賴一個配置,而這個配置在壓測中被降級了,一個 jar 包就把應用啟動卡住了。如果在雙十一當天或者在平時業務高峰期的時候發現這個問題是來不及修復的。所以這個時候,我們就要求,依賴的二方 jar 包必須看一下裡面是怎麼實現的,依賴哪些東西。

反過來說,別人依賴我的客戶端就意味著他不僅依賴著我的服務還依賴著我的緩存,這個緩存出了問題對他也有影響,我們每年雙十一前有一個強弱依賴梳理,不僅要梳理自己應用裡面的,還有依賴的所有東西都梳理出來,中間任何一個節點掛掉了你應該怎麼辦,需要給一個明確答覆。

第二個故障案例是今年發生的,AWS S3 敲錯了一個命令把基礎核心服務下線了,有一個對象索引服務和位置服務系統被 offline,後來也做了一些改進,每次敲的命令有一個靜默期,讓你有個反悔的機會,線上有個最小的資源保證服務。

這個給我們帶來的啟示是什麼,雲服務本身也是會發生故障的,比如買了雲資料庫,我們沒有辦法假設它是 100% 可用的,當它出現問題我們怎麼辦,是給雲廠商提工單說什麼時候能恢復,還是我自己能夠有一個容災的方案解決這個問題。從 2015 年開始,我們越來越多地發現,對架構可用性最大的威脅是什麼?在市政施工里一定幾率就會莫名其妙搞出光纜被挖斷等故障,我們不得不考慮,當雲服務本身出現問題我們該怎麼辦。

應對措施

所以我們需要有一套面向雲的高可用架構。在很早以前有廠商提出類似 SDN 的一個概念,叫 SDI——軟體定義基礎設施,過去我們發現只有大廠可以做這個事情,設計一套很複雜的管理系統幫他實現,這裡放一個路由器,這邊放一台虛擬機,可以通過軟體的控制流去控制這個東西。但是在雲的時代,資源變得很容易獲得。以阿里云為例子,可以用 API 隨時創建新的虛擬機,新的負載均衡,或者是新的存儲,都可以通過 API 隨時創建隨時銷毀,這對於你的基礎設施的靈活度非常有好處。

以前有的人會覺得,性能問題或者容量問題應該通過性能優化的方式解決,通過一些黑科技方式解決,加機器會覺得很 low。但我覺得一個問題如果能簡單用加機器來解決是很不容易的,意味著對你的整個架構的水平擴展性要求非常高,而且解決效率很高,加機器就解決了,而對一些中心化的系統來說就比較麻煩,加機器都加不了,可能要把機器關掉升配置再重新拉起來。所以我們說,在公有雲上面,在資源如此容易獲得的情況下要充分利用這個特性,要做一個能夠做水平擴展的架構。

那麼第一步要做什麼,前兩年很火的容器、微服務,本質上都是解決了是無狀態的應用怎麼做自動化的擴容這個問題。右邊這個圖上面,上面是一個負載均衡,中間是一個前端的服務,後端是一個無狀態的後端服務,底層是 MQ、對象存儲、資料庫這些東西,如果我們能夠把前端和後端的無狀態服務第一步先容器化,就可以做到當流量過來的時候,只要後端的存儲沒有問題,整套架構就是能夠水平擴展的。

從去年公開的報道和故障來看,很多人有個誤會是說雲廠商的機器應該是不會掛的,我買了一台雲廠商的虛擬機應該是隨時可用的,即使不可用雲廠商也要幫我解決熱遷移的問題,熱遷移在業界是很複雜的問題,不光涉及到磁碟存儲的遷移,也涉及到內存是要做遷移的,可能還要用 RDMA。並且對於傳統的 IDC 來說,不管物理機還是虛擬機都是有可能掛的,對雲也不例外。

當我們在使用公有雲服務的時候,都是會掛的,這是個心理準備。不光是機器,包括負載均衡是不是也有可能掛,下面的消息隊列或者資料庫是不是也有可能會掛,當你基於任何東西都可能會掛的前提設計一個系統的時候,才能真正做到這個系統在任何情況下都不會受底層雲服務的故障影響。

而對於不同的雲服務來說是有不同的容災策略。比如一台虛擬機掛了,通常來說負載均衡不管是 4 層還是 7 層都會做健康檢查,掛了健康檢查不通自動會把流量切斷。如果我的負載均衡掛了怎麼辦,如果 DNS 有健康檢查那就方便了,如果沒有的話可能就要設計一個旁路系統解決這個問題,發現這個已經不通了,就自動把它從 DNS 上摘掉。

不管是雲服務發生故障還是自己應用發生故障有個大原則是如何最快速解決問題,就是一個字,切。為什麼要做異地多活,為什麼要把流量往各個地方引,切流量是解決問題最快的,把壞流量切到好的地方馬上就解決了,如果你要等定位問題解決問題再上線客戶就流失掉了。對淘寶來說也是一樣,當某一個單元低於我們認為的可用性的時候,我們會把這個單元的流量引到另外一個可用的單元,當然前提是那個單元的容量是足夠的。

彈性是不是萬能的?所有的雲服務都是彈性的,彈性其實不是萬能的,容量規劃仍然是有必要的,不然就沒必要做雙十一備戰了。這裡有一個你需要付出的代價,彈性的過程往往是需要時間的,那麼容量規劃在這個環節中起到的作用就很重要,當真的逼不得已的時候,我要擴容了,怎麼保證我擴完容之前系統不雪崩?就是要結合之前的限流,儘可能保障每個客戶得到他應有的服務,但是也要保障系統的穩定性。

Region 和 Availability Zone 這兩個,當實踐的時候,購買虛擬機、負載均衡或者資料庫,一定要選擇多可能區的服務,比如阿里雲買 SLB 是可選可用區的,有個主可用區和副可用區,如果一邊掛了可以切換到另外一邊,RDS 也是一樣的。

這幅圖是一套典型基於公有雲的一套架構,不叫異地多活,應該叫跨區域設計。左右兩個大框是兩個城市,左邊是北京,右邊是上海,每個裡面又有不同的機房可用區去承擔這個流量,假如北京的掛掉了,就切,原來是在 A 可用區,就切到 B 可用區,這時候 A 可用區沒有流量進來,通過切換的方式能夠把這個服務快速恢復。下面畫了一個跨區域複製,我們在異地多活項目里,涉及到了跨城市跨數據中心的複製,比如我的北京是提供寫服務的,上海要提供讀服務,就要通過這種方式同步數據過去。

作者介紹

王晨純,花名沐劍,阿里巴巴技術專家。在阿里長期負責高可用相關領域工作,包括評價、店鋪、商家事業部等雙 11 技術保障工作,從 2015 年開始,在阿里百川業務,基於阿里雲,為移動互聯網應用提供高可用技術及產品,參與了 EWS 等高可用領域產品架構設計,積累了雲上和雲下的全面穩定性經驗。

推薦一個沙龍

百度技術沙龍:開源資料庫 Tera 架構與搜索引擎的運用 免費報名——Tera 是百度網頁搜索部開源的大型分散式表格系統,其數十 PB 量級的數據存儲和百萬 QPS 的實時數據訪問能力,支撐著百度搜索引擎的鏈接存儲、實時索引篩選、實時用戶行為分析等多個核心繫統。本次技術分享為大家深入介紹 Tera 的架構設計與實踐以及 Tera 在百度搜索引擎中的應用。

識別下圖「二維碼 」或戳 「 閱讀原文 」,即刻報名!

技術漫談:為何 KPI 毀了索尼,而 OKR 卻成就了谷歌?



熱門推薦

本文由 yidianzixun 提供 原文連結

寵物協尋 相信 終究能找到回家的路
寫了7763篇文章,獲得2次喜歡
留言回覆
回覆
精彩推薦