作者 | 魏磊 心澎 陳彤
CDN已經(jīng)成為互聯(lián)網(wǎng)重要的基建之一,越來越多的網(wǎng)絡服務離不開CDN,它的穩(wěn)定性也直接影響到業(yè)務的可用性。CDN的容災一直由美團的SRE團隊在負責,在端側鮮有方案和實踐。
本文結合美團外賣業(yè)務中的具體實踐,介紹了一種在端側感知CDN可用性狀況并進行自動容災切換的方案,通過該方案可有效降低業(yè)務對CDN異常的敏感,提高業(yè)務的可用性,同時降低CDN運維壓力。希望本方案能夠?qū)Ρ籆DN問題所困擾的同學有所幫助或者啟發(fā)。
1. 前言
作為業(yè)務研發(fā),你是否遇到過因為 CDN 問題導致的業(yè)務圖片加載失敗,頁面打開緩慢,頁面布局錯亂或者頁面白屏?你是否又遇到過某些區(qū)域 CDN 域名異常導致業(yè)務停擺,客訴不斷,此時的你一臉茫然,不知所措?作為 CDN 運維,你是否常常被業(yè)務方反饋的各種 CDN 問題搞得焦頭爛額,一邊頂著各種催促和壓力尋求解決方案,一邊抱怨著服務商的不靠譜?今天,我們主要介紹一下美團外賣技術團隊端側 CDN 的容災方案,經(jīng)過實踐,我們發(fā)現(xiàn)該產(chǎn)品能有效減少運維及業(yè)務開發(fā)同學的焦慮,希望我們的這些經(jīng)驗也能夠幫助到更多的技術團隊。
2. 背景
CDN 因能夠有效解決因分布、帶寬、服務器性能帶來的網(wǎng)絡訪問延遲等問題,已經(jīng)成為互聯(lián)網(wǎng)不可或缺的一部分,也是前端業(yè)務嚴重依賴的服務之一。在實際業(yè)務生產(chǎn)中,我們通常會將大量的靜態(tài)資源如 JS 腳本、CSS 資源、圖片、視頻、音頻等托管至 CDN 服務,以享受其邊緣節(jié)點緩存對靜態(tài)資源的加速。但是在享用 CDN 服務帶來更好體驗的同時,也經(jīng)常會被 CDN 故障所影響。比如因 CDN 邊緣節(jié)點異常,CDN 域名封禁等導致頁面白屏、排版錯亂、圖片加載失敗。每一次的 CDN 故障,業(yè)務方往往束手無策,只能寄希望于 CDN 團隊。而 CDN 的監(jiān)控與問題排查,對 SRE 也是巨大的難題和挑戰(zhàn)。一方面,由于 CDN 節(jié)點的分布廣泛,邊緣節(jié)點的監(jiān)控就異常困難。另一方面,各業(yè)務匯聚得到的 CDN 監(jiān)控大盤,極大程度上隱匿了細節(jié)。小流量業(yè)務、定點區(qū)域的 CDN 異常往往會被淹沒。SRE 團隊也做了很多努力,設計了多種方案來降低 CDN 異常對業(yè)務的影響,也取得了一定的效果,但始終有幾個問題無法很好解決:
- 時效性:當 CDN 出現(xiàn)問題時,SRE 會手動進行 CDN 切換,因為需要人為操作,響應時長就很難保證。另外,切換后故障恢復時間也無法準確保障。
- 有效性:切換至備份 CDN 后,備份 CDN 的可用性無法驗證,另外因為 Local DNS 緩存,無法解決域名劫持和跨網(wǎng)訪問等問題。
- 精準性:CDN 的切換都是大范圍的變更,無法針對某一區(qū)域或者某一項目單獨進行。
- 風險性:切換至備份 CDN 之后可能會導致回源,流量劇增拖垮源站,從而引發(fā)更大的風險。
當前,美團外賣業(yè)務每天服務上億人次,即使再小的問題在巨大的流量面前,也會被放大成大問題。外賣的動態(tài)化架構,70%的業(yè)務資源都依賴于 CDN,所以 CDN 的可用性嚴重影響著外賣業(yè)務。如何更有效的進行 CDN 容災,降低 CDN 異常對業(yè)務的影響,是我們不斷思考的問題。既然以上問題 SRE 側無法完美地解決,端側是不是可以進行一些嘗試呢?比如將 CDN 容災前置到終端側。不死鳥(Phoenix) 就是在這樣的設想下,通過前端能力建設,不斷實踐和完善的一套端側 CDN 容災方案。該方案不僅能夠有效降低 CDN 異常對業(yè)務的影響,還能提高 CDN 資源加載成功率,現(xiàn)已服務整個美團多個業(yè)務和 App。
3. 目標與場景
3.1 核心目標
為降低 CDN 異常對業(yè)務的影響,提高業(yè)務可用性,同時降低 SRE 同學在 CDN 運維方面的壓力,在方案設計之初,我們確定了以下目標:
- 端側 CDN 域名自動切換:在 CDN 異常時,端側第一時間感知并自動切換 CDN 域名進行加載重試,減少對人為操作的依賴。
- CDN 域名隔離:CDN 域名與服務廠商在區(qū)域維度實現(xiàn)服務隔離且服務等效,保證 CDN 切換重試的有效性。
- 更精準有效的 CDN 監(jiān)控:建設更細粒度的 CDN 監(jiān)控,能夠按照項目維度實時監(jiān)控 CDN 可用性,解決 SRE CDN 監(jiān)控粒度不足,告警滯后等問題。并根據(jù)容災監(jiān)控對 CDN 容災策略實施動態(tài)調(diào)整,減少 SRE 切換 CDN 的頻率。
- 域名持續(xù)熱備:保證每個 CDN 域名的持續(xù)預熱,避免流量切換時導致回源。
3.2 適用場景
適用所有依賴 CDN ,希望降低 CDN 異常對業(yè)務影響的端側場景,包括 Web、SSR Web、Native 等技術場景。
4. Phoenix 方案
一直以來,CDN 的穩(wěn)定性是由 SRE 來保障,容災措施也一直在 SRE 側進行,但僅僅依靠鏈路層面上的保障,很難處理局部問題和實現(xiàn)快速止損。用戶終端作為業(yè)務的最終投放載體,對資源加載有著天然的獨立性和敏感性。如果將 CDN 容災前置到終端側,無論從時效性,精準性,都是 SRE 側無法比擬的。在端側進行容災,就需要感知 CDN 的可用性,然后實現(xiàn)端側自動切換的能力。我們調(diào)研整個前端領域,并未發(fā)現(xiàn)業(yè)內(nèi)在端側 CDN 容災方面有所實踐和輸出,所以整個方案的實現(xiàn)是從無到有的一個過程。
4.1 總體設計
圖 1
Phoenix 端側 CDN 容災方案主要由五部分組成:
- 端側容災 SDK:負責端側資源加載感知,CDN 切換重試,監(jiān)控上報。
- 動態(tài)計算服務:根據(jù)端側 SDK 上報數(shù)據(jù),對多組等效域名按照城市、項目、時段等維度定時輪詢計算域名可用性,動態(tài)調(diào)整流量至最優(yōu) CDN。同時也是對 CDN 可用性的日常巡檢。
- 容災監(jiān)控平臺:從項目維度和大盤維度提供 CDN 可用性監(jiān)控和告警,為問題排查提供詳細信息。
- CDN 服務:提供完善的 CDN 鏈路服務,在架構上實現(xiàn)域名隔離,并為業(yè)務方提供等效域名服務,保證端側容災的有效性。等效域名,就是能夠通過相同路徑訪問到同一資源的域名,比如:cdn1.meituan.net/src/js/test.js 和 cdn2.meituan.net/src/js/test.js 能夠返回相同內(nèi)容,則 cdn1.meituan.net 和 cdn2.meituan.net 互為等效域名。
- 容災配置平臺:對項目容災域名進行配置管理,監(jiān)控上報策略管理,并提供 CDN 流量人工干預等措施。
4.2 容災流程設計
為保證各個端側容災效果和監(jiān)控指標的一致性,我們設計了統(tǒng)一的容災流程,整體流程如下:
圖 2
4.3 實現(xiàn)原理
4.3.1 端側容災 SDK
Web 端實現(xiàn)
Web 端的 CDN 資源主要是 JS、CSS 和圖片,所以我們的容災目標也聚焦于這些。在 Web 側的容災,我們主要實現(xiàn)了對靜態(tài)資源,異步資源和圖片資源的容災。實現(xiàn)思路要實現(xiàn)資源的容災,最主要的問題是感知資源加載結果。通常我們是在資源標簽上面添加錯誤回調(diào)來捕獲,圖片容災可以這樣實現(xiàn),但這并不適合 JS,因為它有嚴格的執(zhí)行順序。為了解決這一問題,我們將傳統(tǒng)的標簽加載資源的方式,換成XHR來實現(xiàn)。通過Webpack在工程構建階段把同步資源進行抽離,然后通過PhoenixLoader來加載資源。這樣就能通過網(wǎng)絡請求返回的狀態(tài)碼,來感知資源加載結果。在方案的實現(xiàn)上,我們將 SDK 設計成了 Webpack Plugin,主要基于以下四點考慮:
- 通用性:美團前端技術棧相對較多,要保證容災 SDK 能夠覆蓋大部分的技術框架。
- 易用性:過高的接入成本會增加開發(fā)人員的工作量,不能做到對業(yè)務的有效覆蓋,方案價值也就無從談起。
- 穩(wěn)定性:方案要保持穩(wěn)定可靠,不受 CDN 可用性干擾。
- 侵入性:不能侵入到正常業(yè)務,要做到即插即用,保證業(yè)務的穩(wěn)定性。
通過調(diào)研發(fā)現(xiàn),前端有 70%的工程構建都離不開 Webpack,而 Webpack Plugin 獨立配置,即插即用的特性,是實現(xiàn)方案的最好選擇。整體方案設計如下:
圖 3
當然,很多團隊在做性能優(yōu)化時,會采取代碼分割,按需引入的方式。這部分資源在同步資源生成的過程中無法感知,但這部分資源的加載結果,也關系到業(yè)務的可用性。在對異步資源的容災方面,我們主要是通過對 Webpack 的異步資源處理方式進行重寫,使用Phoenix Loader接管資源加載,從而實現(xiàn)異步資源的容災。整體分析過程如下圖所示:
圖 4
CSS 資源的處理與 JS 有所差別,但原理相似,只需要重寫 mini-css-extract-plugin 的異步加載實現(xiàn)即可。Web 端方案資源加載示意:
圖 5
容災效果
圖6 容災大盤
圖7 容災案例
Native 端容災
客戶端的 CDN 資源主要是圖片,音視頻以及各種動態(tài)化方案的 bundle 資源。Native 端的容災建設也主要圍繞上述資源展開。實現(xiàn)思路重新請求是 Native 端 CDN 容災方案的基本原理,根據(jù)互備 CDN 域名,由 Native 容災基建容災域名重新進行請求資源,整個過程發(fā)生在原始請求失敗后。Native 容災基建不會在原始請求過程中進行任何操作,避免對原始請求產(chǎn)生影響。原始請求失敗后,Native 容災基建代理處理失敗返回,業(yè)務方仍處于等待結果狀態(tài),重請新求結束后向業(yè)務方返回最終結果。整個過程中從業(yè)務方角度來看仍只發(fā)出一次請求,收到一次結果,從而達到業(yè)務方不感知的目的。為將重新請求效率提升至最佳,必須盡可能的保證重新請求次數(shù)趨向于最小。調(diào)研業(yè)務的關注點和技術層面使用的網(wǎng)絡框架,結合 Phoenix 容災方案的基本流程,在方案設計方面,我們主要考慮以下幾點:
- 便捷性:接入的便捷性是 SDK 設計時首先考慮的內(nèi)容,即業(yè)務方可以用最簡單的方式接入,實現(xiàn)資源容災,同時也可以簡單無殘留拆除 SDK。
- 兼容性:Android 側的特殊性在于多樣的網(wǎng)絡框架,集團內(nèi)包括 Retrofit 框架,okHttp 框架,okHttp3 框架及已經(jīng)很少使用的 URLConnection 框架。提供的 SDK 應當與各種網(wǎng)絡框架兼容,同時業(yè)務方在即使變更網(wǎng)絡框架也能夠以最小的成本實現(xiàn)容災功能。而 iOS 側則考慮復用一個 NSURLProtocol 去實現(xiàn)對請求的攔截,降低代碼的冗余度,同時實現(xiàn)對初始化項進行統(tǒng)一適配。
- 擴展性:需要在基礎功能之上提供可選的高級配置來滿足特殊需求,包括監(jiān)控方面也要提供特殊的監(jiān)控數(shù)據(jù)上報能力。
基于以上設計要點,我們將 Phoenix 劃分為以下結構圖,圖中將整體的容災 SDK 拆分為兩部分 Phoenix-Adaptor 部分與 Phoenix-Base 部分。
圖 8
Phoenix-BasePhoenix-Base 是整個 Phoenix 容災的核心部分,其包括容災數(shù)據(jù)緩存,域名更換組件,容災請求執(zhí)行器(區(qū)別于原始請求執(zhí)行器),監(jiān)控器四個對外不可見的內(nèi)部功能模塊,并包含外部接入模塊,提供外部接入功能。
- 容災數(shù)據(jù)緩存:定期獲取及更新容災數(shù)據(jù),其產(chǎn)生的數(shù)據(jù)只會被域名更換組件使用。
- 域名更換組件:連接容災數(shù)據(jù)緩存,容災請求執(zhí)行器,監(jiān)控器的中心節(jié)點,負責匹配原始失敗 Host,過濾錯誤碼,并向容災請求執(zhí)行器提供容災域名,向監(jiān)控器提供整個容災過程的詳細數(shù)據(jù)副本。
- 容災執(zhí)行器:容災請求的真正請求者,目前采用內(nèi)部 OkHttp3Client,業(yè)務方也可以自主切換至自身的執(zhí)行器。
- 監(jiān)控器:分發(fā)容災過程的詳細數(shù)據(jù),內(nèi)置數(shù)據(jù)大盤的上報,若有外部自定義的監(jiān)控器,也會向自定義監(jiān)控器分發(fā)數(shù)據(jù)。
Phoenix-AdaptorPhoenix-Adaptor 是 Phoenix 容災的擴展適配部分,用于兼容各種網(wǎng)絡框架。
- 綁定器:生成適合各個網(wǎng)絡框架的攔截器并綁定至原始請求執(zhí)行者。
- 解析器:將網(wǎng)絡框架的 Request 轉換為 Phoenix 內(nèi)部執(zhí)行器的 Request,并將 Phoenix 內(nèi)部執(zhí)行器的 Response 解析為外部網(wǎng)絡框架 Response,以此達到適配目的。
容災效果① 業(yè)務成功率以外賣圖片業(yè)務為例,Android 業(yè)務成功率對比(同版本 7512,2021.01.17 未開啟 Phoenix 容災,2021.01.19 晚開啟 Phoenix 容災)。
圖 9iOS 業(yè)務成功率對比(同版本 7511,2021.01.17 未開啟 Phoenix 容災,2021.01.19 晚開啟 Phoenix 容災)。
圖 10② 風險應對以外賣與美團圖片做為對比 ,在 CDN 服務出現(xiàn)異常時,接入 Phoenix 的外賣 App 和未接入的美團 App 在圖片成功率方面的對比。
圖 11
4.3.2 動態(tài)計算服務
端側的域名重試,會在某一域名加載資源失敗后,根據(jù)容災列表依次進行重試,直至成功或者失敗。如下圖所示:
圖 12如果域名 A 大范圍異常,端側依然會首先進行域名 A 的重試加載,這樣就導致不必要的重試成本。如何讓資源的首次加載更加穩(wěn)定有效,如何為不同業(yè)務和地區(qū)動態(tài)提供最優(yōu)的 CDN 域名列表,這就是動態(tài)計算服務的要解決的問題。計算原理動態(tài)計算服務通過域名池和項目的 Appkey 進行關聯(lián),按照不同省份、不同地級市、不同項目、不同資源等維度進行策略管理。通過獲取 5 分鐘內(nèi)對應項目上報的資源加載結果進行定時輪詢計算,對域名池中的域名按照地區(qū)(城市&&省份)的可用性監(jiān)控。計算服務會根據(jù)域名可用性動態(tài)調(diào)整域名順序并對結果進行輸出。下圖是一次完整的計算過程:
圖 13
假設有 A、B、C 三個域名,成功率分別是 99%、98%、97.8%,流量占比分別是 90%、6%、4%。基于轉移基準,進行流量轉移,比如,A 和 B 成功率差值是 1,B 需要把自己 1/2 的流量轉移給 A,同時 A 和 C 的成功率差值大于 1,C 也需要把自己 1/2 的流量轉移給 A,同時 B 和 C 的差值是 0.2,所以 C 還需要把自己 1/4 的流量轉移給 B。最終,經(jīng)過計算,A 的流量占比是 95%,B 是 3.5%,C 是 1.5%。最后,經(jīng)過排序和隨機計算后將最終結果輸出。因為 A 的占比最大,所以 A 優(yōu)先被選擇;通過隨機,B 和 C 也會有一定的流量;基于轉移基準,可以實現(xiàn)流量的平穩(wěn)切換。異常喚起當某個 CDN 無法正常訪問的時候,該 CDN 訪問流量會由計算過程切換至等效的 CDN B。如果 SRE 發(fā)現(xiàn)切換過慢可以進行手動干預分配流量。當少量的 A 域名成功率上升后,會重復計算過程將 A 的流量加大。直至恢復初始態(tài)。
圖 14
服務效果動態(tài)計算服務使得資源的首次加載成功率由原來的99.7%提升至99.9%。下圖為接入動態(tài)計算后資源加載成功率與未接入加載成功率對比。
圖 15
4.3.3 容災監(jiān)控
在監(jiān)控層面,SRE 團隊往往只關注域名、大區(qū)域、運營商等復合維度的監(jiān)控指標,監(jiān)控流量巨大,對于小流量業(yè)務或者小范圍區(qū)域的 CDN 波動,可能就無法被監(jiān)控分析識別,進而也就無法感知 CDN 邊緣節(jié)點異常。容災監(jiān)控建設,主要是為了解決 SRE 團隊的 CDN 監(jiān)控告警滯后和監(jiān)控粒度問題。監(jiān)控整體設計如下:
圖 16流程設計端側容災數(shù)據(jù)的上報,分別按照項目、App、資源、域名等維度建立監(jiān)控指標,將 CDN 可用性變成項目可用性的一部分。通過計算平臺對數(shù)據(jù)進行分析聚合,形成 CDN 可用性大盤,按照域名、區(qū)域、項目、時間等維度進行輸出,與天網(wǎng)監(jiān)控互通,建立分鐘級別的監(jiān)控告警機制,大大提升了 CDN 異常感知的靈敏性。同時,SRE 側的天網(wǎng)監(jiān)控,也會對動態(tài)計算服務結果產(chǎn)生干預。監(jiān)控整體流程如下:
圖 17監(jiān)控效果CDN 監(jiān)控不僅從項目維度更加細粒度的監(jiān)測 CDN 可用性,還為 CDN 異常排查提供了區(qū)域、運營商、網(wǎng)絡狀況、返回碼等更豐富的信息。在監(jiān)控告警方面,實現(xiàn)了分鐘級異常告警,靈敏度也高于美團內(nèi)部的監(jiān)控系統(tǒng)。
圖 18
4.3.4 CDN 服務
端側域名切換的有效性,離不開 CDN 服務的支持。在 CDN 服務方面,在原有 SRE 側容災的基礎上,對 CDN 服務整體做了升級,實現(xiàn)域名隔離,解決了單域名對應多 CDN 和多域名對應單 CDN 重試無效的弊端。
圖 19
5. 總結與展望
經(jīng)過一年的建設與發(fā)展,Phoenix CDN 容災方案日趨成熟,現(xiàn)已成為美團在 CDN 容災方面唯一的公共服務,在多次 CDN 異常中發(fā)揮了巨大的作用。在端側,當前該方案日均容災資源3000萬+,挽回用戶35萬+,覆蓋外賣,酒旅,餐飲,優(yōu)選,買菜等業(yè)務部門,服務200+個工程,外賣 App、美團 App、大眾點評 App均已接入。在 SRE 側,實現(xiàn)了項目維度的分鐘級精準告警,同時豐富了異常信息,大大提高了 SRE 問題排查效率。自從方案大規(guī)模落地以來,CDN 異常時鮮有手動切換操作,極大減輕了 SRE 同學的運維壓力。由于前端技術的多樣性和復雜性,我們的 SDK 無法覆蓋所有的技術方案,所以在接下來的建設中,我們會積極推廣我們的容災原理,公開動態(tài)計算服務,希望更多的框架和服務在我們的容災思想上,貼合自身業(yè)務實現(xiàn)端側的 CDN 容災。另外,針對方案本身,我們會不斷優(yōu)化資源加載性能,完善資源驗簽,智能切換等能力,也歡迎對 Phoenix CDN 容災方案有興趣的同學,跟我們一起探討交流。同時更歡迎加入我們,文末附招聘信息,期待你的郵件。
6. 作者簡介
魏磊、陳彤、張群、粵俊等,均來自美團外賣平臺-大前端團隊,丁磊、心澎,來自美團餐飲 SaaS 團隊。