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

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

注:本文整理自阿里技術專家沐劍在 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 可用區沒有流量進來,經過切換的方式可以把這個服務快速恢復。下面畫了一個跨區域複製,咱們在異地多活項目裏,涉及到了跨城市跨數據中心的複製,好比個人北京是提供寫服務的,上海要提供讀服務,就要經過這種方式同步數據過去。

相關文章
相關標籤/搜索