1. 程式人生 > >微博CacheService架構淺析(轉)

微博CacheService架構淺析(轉)

作者 麥俊生 釋出於 2014年4月19日 

微博作為國內最大的社交媒體網站之一,每天承載著億萬使用者的服務請求,這些請求的背後,需要消耗著巨大的計算、記憶體、網路、I/O等資源。而且因為微博的產品特性,節假日、熱門事件等可能帶來突發數倍甚至十幾倍的訪問峰值,這些都對於支撐微博的底層基礎架構提出了比較嚴苛的要求,需要滿足:

  1. 每秒數十萬的使用者請求
  2. 資料更新的實時性
  3. 服務請求的低響應時間
  4. 99.99%以上的服務可用性

為了滿足業務的發展需要,微博平臺開發了一套高效能高可用的CacheService架構用於支撐現有線上的業務系統的運轉。但“冰動三尺非一日之寒”,微博的Cache架構也是經歷了從無到有,不斷的演進過程。

基於MySQL的Web架構

最初的微博系統,系統的訪問量都比較小,簡單的基於資料庫(MySQL)已經能夠滿足業務需求,開發也比較簡單,簡單的架構示意圖如下:

隨著微博的推廣和名人使用者入駐微博,帶動了使用者量的快速增長,訪問量也與日俱增,這個時候,簡單基於MySQL的架構已經略感吃力,系統響應也比較緩慢,因為MySQL是一個持久化儲存的解決方案,資料的讀寫都會經過磁碟,雖然MySQL也有buffer pool,但是無法根據業務的特性做到很細粒度的控制,而在微博這種業務場景下,配置了SAS盤的MySQL服務單機只能支撐幾千的請求量,遠小於微博的業務請求量。

基於單層Cache+MySQL的Web架構

針對請求量增大的問題,一般有幾種解決方案:

  1. 業務架構改造,但是在這種場景下,這種方案的可行性不高。
  2. MySQL進行從庫擴容,雖然能夠解決問題,但是帶來的成本也會比較高,而且即使能夠抗住請求量,但是資源的響應時間還是無法滿足期望的結果,因為磁碟的讀取的響應時間要相對比較慢,普通的15000轉/分鐘的SAS盤的讀取延遲平均要達到2ms以上。
  3. 在MySQL之上架構一層快取,把熱門請求資料快取到Cache,基於Cache+MySQL的架構來提供服務請求。

相關廠商內容

相關贊助商

CNUTCon全球運維技術大會,11月16日-17日,上海,精彩內容搶先看

考慮到整體的改動和成本的因素,基於方案3)比較適合微博的業務場景。而應該使用什麼型別的Cache比較合適呢?

比較常見的Cache解決方案有:

  1. Local Cache,通過在Web應用端內嵌一個本地的Cache,這種的優勢是訪問比較快,但是存在的問題也比較明顯,資料更新的一致性比較難保證,因此使用的範圍會有一定的限制。
  2. 單機版的遠端Cache,通過部署一套遠端的Cache服務,然後應用端請求通過網路請求與Cache互動,為了解決應用的水平擴充套件和容災問題,往往通過在client層面來實現資料的路由等。
  3. 分散式的Cache,Cache服務本身是一個大叢集,能夠提供給各種業務應用使用,並提供了一些基本的分散式特性:水平擴充套件、容災、資料一致性等等。

從系統的簡單性考慮和微博場景的適用問題,最終選擇了2)的方式,基於開源的Memcached來作為微博的Cache方案。

Memcached是一個分散式Cache Server,提供了key-value型資料的快取,支援LRU、資料過期淘汰,基於Slab的方式管理記憶體塊,提供簡單的set/get/delete等操作協議,本身具備了穩定、高效能等優點,並在業界已經得到廣泛的驗證。它的server端本身是一個單機版,而分散式特性是基於client端的實現來滿足,通過部署多個Memcached節點,在client端基於一致性hash(或者其他hash策略)進行資料的分散路由,定位到具體的memcached節點再進行資料的互動。當某個節點掛掉後,對該節點進行摘除,並把該節點的請求分散到其他的節點。通過client來實現一定程度的容災和伸縮的能力。

這種架構經過一段時間的蜜月期後,也逐步遇到了一些問題。

  • 節點掛掉導致的瞬間的峰值問題

    比如部署有5個Memcached節點,對key做一致性hash將key散落分佈到5個節點上,那麼如果其中有1個節點掛掉,那麼這個時候會有20%原本Cache hit的請求穿透到後端資源(比如DB)。對於微博而言,多數核心資源的Cache hit的比例是99%,單組資源的QPS可能就達到100W以上的級別,如果這個時候有20%的穿透,那麼相當於後端資源需要抗住20W以上的請求,這對於後端資源來說,明顯壓力過大。

  • 某組資源請求量過大導致需要過多的節點

    微博的Feed業務是Cache資源的消耗大戶,幾十萬的QPS,GB(Byte)級別以上的頻寬消耗,這個時候,至少需要十幾個Memcached節點單元才能夠抗住請求,而過多的Memcached節點請求會導致multiget的效能有弱化,因為這個時候keys分散到的Memcached節點會比較多,因此當進行拉取聚合的時候,效能會受影響,同時mutliget的響應時間受最慢的那個節點的影響,從而無法達到服務的SLA要求。

  • Cache的伸縮容和節點的替換動靜太大

    對於微博這種會在熱點事件、節假日等發生時會有一些變態峰值(往往是數倍或者數十倍)的場景而言,實時的動態伸縮容很是必要,而因為通過client端例項化的Memcached資源節點相對比較固定,因此要進行伸縮容需要:

    1. 進行一次程式碼的線上變更,進行節點配置的變更,而如果依賴該某組資源的應用系統比較多,比如底層的認證資源,那麼需要對多個業務系統變更,這一動靜不可謂不小,特別是遇到緊急情況,這個會導致操作的執行很緩慢。
    2. 需要解決讀寫導致的一致性問題,假如有一些業務系統在讀取Cache,有一些業務系統在寫入Cache,而正常的變更是比較難讓這些系統在某一刻全部執行節點的配置切換。
    3. 需要使用新的節點替換老的節點(比如更換物理機),面臨和上面類似的問題。
  • 過多資源帶來的運維問題

Cache資源組是按業務去申請,當業務特別多的時候,Cache資源組也會很多,這個時候要對這些資源進行運維管理如調整,將會變得不容易。而且隨著時間的演進,一些比較古老的資源年老失修的情況,要進行運維調整就更為不容易。

  • Cache架構要用得好的複雜度

會用和用得好是兩個不同概念。如果Cache架構需要每個業務開發很熟練才能夠用得好,而不會因為Cache的不當使用而導致線上服務出現穩定性問題、以及成本的浪費等各種問題的話,這種對於需要陸續補進新人的團隊現狀而言,出問題將會是一種常態。 因此要解決這種問題,那麼需要提供一種足夠簡單的Cache使用方式給業務應用方,簡單到只有set/get/delete等基本命令的操作,而無需要他們關心底層的任何細節。

分散式CacheService架構

為了解決這些問題,微博的Cache服務架構進行了演進,通過把Cache服務化,提供一個分散式的CacheService架構,簡化業務開發方的使用,實現系統的動態伸縮容、容災、多層Cache等相關功能。

CacheService架構示意圖如下:

系統由幾個模組組成:

  • ConfigService

    這一模組是基於現有微博的配置服務中心,它主要是管理靜態配置和動態命名服務的一個遠端服務,能夠在配置發生變更的時候實時通知監聽的config client。

  • proxy層

    這一模組是作為獨立的應用對外提供代理服務,用來接收來自業務端的請求,並基於路由規則轉發到後端的Cache資源,它本身是無狀態的節點。它包含了如下部分:

    • 非同步事件處理(event handler): 用來管理連線、接收資料請求、回寫響應。
    • Processor: 用來對請求的資料進行解析和處理。
    • Adapter:用來對底層協議進行適配,比如支援MC協議,Redis協議。
    • Router: 用來對請求進行路由分發,分發到對應的Cache資源池,進而隔離不同業務。
    • LRU Cache: 用來優化效能,緩解因為經過proxy多一跳(網路請求)而帶來的效能弱化。
    • Timer: 用來執行一些後端的任務,包含對底層Cache資源健康狀態的探測等。

    Proxy啟動後會去從config Service載入後端Cache資源的配置列表進行初始化,並接收configService的配置變更的實時通知。

  • Cache資源池

    這一模組是作為實際資料快取的模組,通過多層結構來滿足服務的高可用。 其中Main-node是主快取節點,Ha-Node是備份節點,當Main-node掛掉後,資料還能夠從Ha-Node節點獲取避免穿透到後端資源,L1-node主要用來抗住熱點的訪問,它的容量一般比Main-node要小,其中L1-node可支援多組,方便進行水平擴容以支撐更高的吞吐。

  • Client客戶端

    這一模組主要是提供給業務開發方使用的client(sdk包),對外遮蔽掉了所有細節,只提供了最簡單的get/set/delete等協議介面,從而簡化了業務開發方的使用。

    應用啟動時,Client基於namespace從configService中獲取相應的proxy節點列表,並建立與後端proxy的連線。正常一個協議處理,比如set命令,client會基於負載均衡策略挑選當前最小負載的proxy節點,發起set請求,並接收proxy的響應返回給業務呼叫端。

    Client會識別configService推送的proxy節點變更的情況重建proxy連線列表,同時client端也會做一些容災,在proxy節點出現問題的時候,把proxy進行摘除,並定期探測是否恢復。

目前微博平臺部分業務子系統的Cache服務已經遷移到了CacheService之上,它在實際的執行過程中也取得了良好的效能表現,目前整個叢集在線上每天支撐著超過300W的QPS,平均響應耗時低於1ms。

它本身具備了以下特性:

  • 高可用保證

    所有的資料寫入請求,CacheService會把資料雙寫到ha的節點,這樣,在main-node掛掉的時候,會從ha-node讀取資料,從而防止節點fail的時候給後端資源(DB等)帶來過大的壓力。

  • 服務的水平擴充套件

    CacheService proxy節點本身是無狀態的,在proxy叢集存在效能問題的時候,能夠簡單的通過增減節點來伸縮容。而對於後端的Cache資源,通過增減L1層的Cache資源組,來分攤對於main-node的請求壓力。這樣多數熱點資料的請求都會落L1層,而L1層可以方便的通過增減Cache資源組來進行伸縮容。

  • 實時的運維變更

    通過整合內部的config Service系統,能夠在秒級別做到資源的擴容、節點的替換等相關的運維變更。

  • 跨機房特性:

    微博系統會進行多機房部署,跨機房的伺服器網路時延和丟包率要遠高於同機房,比如微博廣州機房到北京機房需要40ms以上的時延。CacheService進行了跨機房部署,對於Cache的查詢請求會採用就近訪問的原則,對於Cache的更新請求支援多機房的同步更新。

目前微博的分散式CacheService架構在簡化了業務開發使用的同時,提高了系統的可運維性和可用性。接下來的架構的改造方向是提供後端Cache資源的低成本解決方案,從單機的儲存容量和單機的極限效能層面不斷優化。因為對於微博的業務場景,冷熱資料相對比較明顯,同時長尾資料請求的比例也不小,因而如果減少了Cache的容量,那麼會導致後端資源無法抗住請求,而擴大Cache的容量,又會導致成本的浪費。而全記憶體的解決方案相比而言成本相對比較高,所以熱資料存放到記憶體,基於LRU的策略把冷資料交換到固體硬碟(SSD),這是一種可能選擇的方向。

感謝崔康對本文的審校。

給InfoQ中文站投稿或者參與內容翻譯工作,請郵件至[email protected]。也歡迎大家通過新浪微博(@InfoQ)或者騰訊微博(@InfoQ)關注我們,並與我們的編輯和其他讀者朋友交流。