天天看點

閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結

作者:閑魚技術——吳白

背景

首頁和搜尋一直以來都是閑魚導購的主陣地,為了保證高可用業務上做了很多保護方案。但是随着原有地域的IDC日漸趨于飽和,一些更深層次的問題開始暴露出來:a)架構不具備擴充性。當服務量增大,單個IDC由于伺服器部署、電力等實體因素無法滿足訴求,不能簡單的通過IDC部署來應對新增的流量。以算法為例,算法在現有IDC資源飽和的情況下,其上新模型之前不得不等老模型下線,嚴重制約業務的疊代效率。b)容災問題。當現有IDC出現故障時,如何保證導購主鍊路依然高可用。

常用高可用架構

  • 同城雙活:系統從接入層以下在同城兩個機房做部署,這樣可以應對斷電,斷網等單機房故障。由于同城機房距離足夠近,可以近似看成一個機房,是以在部署上和單機房相比沒有特殊要求。但是遇到同區域災害時,服務就會受到影響,而且擴充性較差。
閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結
  • 異地災備:系統從接入層以下除了在同城兩個機房做部署之外,在其他區域部署異地備份,底層資料根據實際要求做熱備/冷備,但是不承擔任何流量。當區域挂掉時,服務切至備份區域保證服務可用性。但異地災備的問題是:a)另一個區域不跑流量,出了問題不敢切。b)備份全站,資源使用率低。c)存在跨地域通路。
閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結
  • 異地多活:異地多活從接入層開始做多區域多機房部署,各個區域之間沒有主備的概念,均承擔相應的流量。它的優勢在于資源使用率高,擴充性較好。
閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結

前面提到閑魚除了要解決容災問題之外還需要解決算法同學的資源利用問題,是以異地多活很自然成為我們的不二選擇。

多地部署帶來的變化

當我們的系統從單地部署更新成多地域部署時,它并不是簡單将整個系統搬到各個地域去做部署。當你的系統部署過去之後,需要考慮非常多的因素,比如

  • 流量排程,系統部署過去後流量怎麼跟着怎麼過去。
  • 流量自閉環。由于距離的原因,跨地域的實體延時是沒法避免的,流量過去之後怎麼保證所有的操作都在本地完成,如果做不到那怎麼将這種延時影響降到最低。
  • 資料一緻性,資料如何在多個地域之間保證一緻性,針對一些如交易相關等資料時效性敏感的場景,如何解決多地域之間資料同步時延。
  • 容災切流。當某個機房出現故障時,如何快速把流量無損地切至其他機房。這裡并不是說簡單把流量切過去就完事,由于資料在多區域同步,流量切過去之後能否保證資料的一緻性?

還有很多其他因素,這裡不一一列舉。可以預想到當我們的系統更新成多地域部署架構之後,給整個系統帶來了很大的變化,同時也帶來了相當大的挑戰。

多地域部署方案

面臨的挑戰

異地部署最大的特點是網絡時延較高:一般來說同地域延時2~3ms,同機房延時小于1ms,而跨地域延時一般大于20ms。是以我們首先要解決的問題便是如何降低跨地域對導購鍊路的影響,這也是我們做異地容災的一個大原則,這面臨着幾個難點

閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結
  1. 流量如何做到地域内閉環,流量閉環必然有代價,如何平衡這兩者之間的關系。
  2. 多地域部署帶來的系統架構的複雜度。無論是流量調控,服務路由還是資料讀寫同步,都要在龐大的系統中做好精細化的調控,對系統帶來的複雜度可想而知。
  3. 如何做流量路由。如何識别流量的來源,控制流量的去向。
  4. 系統部署規範。系統在一直不停的演進,如何避免架構快速腐化。
  5. 流量如何做管控。流量調控規則需要統一做監控,管控等,一旦需要切流時,流量如何快速完成調整收斂。

使用者資料是否做拆分

在介紹部署架構之前先講一下閑魚導購鍊路的一個大前提,後面的很多方案都是基于這個大前提之下做抉擇的,那就是存儲層的資料是否要做拆分。

異地部署之後資料會存在多個區域中,區域之間的資料同步存在一定的延時,是以資料是否要做拆分取決于對資料一緻性的要求。如果可以容忍對資料短時間不一緻那麼則不需要做資料拆分。但是在電商某些場景下,比如買家加入購物車操作,如果資料寫在區域A,購物車清單讀的卻是區域B,那麼很有可能就會導緻買家看不到剛加入購物車的商品,這是非常糟糕的體驗。是以在這種場景下就需要保證資料的讀寫都在相同的次元,這種情況下就需要對資料做拆分。

但是前面提到閑魚導購鍊路特點是:a)可以容忍短時間的資料不一緻。b)不涉及到資料庫的寫操作。顯而易見資料拆分顯得并沒有那麼必要,是以我們決定不做資料拆分。

部署架構

整體部署方案中,實體上各個區域是對等的,不存在主備的概念,但是邏輯上還是區分出了中心區域和其他可用區域,這是因為:a)總有部分長尾依賴沒法做多區域部署。b)并不是所有場景(非核心鍊路)都适合做多地域部署。我們把這些長尾依賴統一放在中心區域,做兜底部署。

閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結

流量路由方案

流量路由方案這裡面包含了兩個問題:

  • 流量分發的原則是什麼,解決哪些流量應該到哪個地域。流量分發原則常見的有三種方式:a)完全随機。b)按照地域就近通路。c)按照使用者次元切分。
  • 流量在哪一層做分發,解決使用者請求從哪裡開始分流。

如前面資料拆分部分提到,流量分發理論上需要和資料拆分邏輯保持一緻。由于閑魚底層沒有做資料拆分,是以流量分發原則相對較為靈活。

  1. 最簡單的流量分發原則是完全随機。前面提到資料在多區域之間存在資料同步延時,雖然導購鍊路可以容忍短時間的資料延時,但是我們需要避免使用者連續兩次請求看到的資料存在不一緻(如果倆次請求分别落在不同地域)。
  1. 按照地域就近通路能實作最低的通路延時,但是這種方案最大的問題是地域之間的流量嚴重不均衡,而且在不停變化(正常時段&節假日),這會給整個系統的負載均衡帶來很高的複雜度。
  1. 按照使用者進行切分,保證部分使用者請求會固定路由到某個區域。

在我們導購場景下1和2都不适合,因為都有可能導緻使用者兩次請求看到的資料不一緻,最終我們選擇按照使用者進行切分,這也是公司内部成熟的路由方案。确定了流量分發原則,接下來需要決定流量在哪一層做分發。這點我們考慮了三個可選的方案

  • 方案一,在域名解析階段做不同地域的流量分發。a)這種方式成本較高,需要有獨立的DNS域名,獨立的路由規則。b)當路由規則調整的時候,收斂周期較長(依賴端側的緩存更新)。c)和運作環境綁定,不支援H5,Web等場景。其優點是經過公司内部驗證,相對比較成熟。
閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結
  • 方案二,在統一接入層進行不同地域流量分發。這種方式成本低,可以複用現有的邏輯,隻需要在統一接入層做規則配置,但是部分流量存在跨地域通路(從接入層到業務叢集)。
閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結
  • 方案三,搭建邊緣網關。通過原生的DNS做域名解析,然後就近選擇邊緣網關通路。切流等複雜邏輯放在邊緣網關中完成。這個方案和運作時無關,支援app&小程式&Web等,而且規則調整收斂速度快,擴充能力強。其缺點是需要從0開始搭基建。
閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結

方案一的跨地域問題是我們需要避免的,方案三雖然比較适合,但是成本太高,而且沒有成熟的經驗借鑒,權衡之下我們最終決定采用方案二。

全鍊路更新改造

全鍊路改造的目的在于使我們的系統适應從單地部署到多地域部署的轉變,改造涉及到的點非常多,主要包括

  1. 應用代碼改造。導購鍊路所有的依賴是否都能做多地部署,如果沒法多地部署跨地域時延是否會被放大。
  2. 服務之間的流量路由政策。導購鍊路涉及到很多異構的子系統,這些異構系統之間的流量是否遵循同地域優先,當某個地域服務挂了之後流量是否允許自動切到其餘地域。
  3. 流量強糾偏。導購的請求鍊路較為複雜,會依賴衆多異構的子系統。雖然域名解析時流量會路由至對應的區域,但是在後續鍊路仍然有可能發生流量竄到其餘地域的情況,這種情況下理論上會對使用者體驗造成影響,是以在導購鍊路的每一跳節點都應該有糾偏政策。
  4. 外部流量由于分發政策我們沒法管控,會導緻預期之外的流量流入。為了避免這種情況,我們也需要有一個流量糾偏的政策。

改造點3在資料強一緻場景是必不可少的,但是對本次導購鍊路,由于改造成本和時間的關系,最終我們放棄了改造點3。因為改造點2保證了正常情況下流量路徑是符合預期的,隻有異常情況才有可能發生流量竄到其他區域,但是這種情況我們認為:a)低頻且持續時間不長。b)短時間的不一緻對業務影響可控。

應用代碼改造主要包括

  • 對于那些沒法做多地部署的依賴,評估其對資料一緻性的限制,如果是弱一緻性,則考慮使用富用戶端模式,在富用戶端模式中優先讀緩存,不命中再走一次RPC,通過緩存降低跨地域請求的頻率。
  • 沒法做多地部署且要求資料強一緻性的依賴,需要避免跨地域通路時延被放大。不存在跨地域延時的時候串行并行的差別并不明顯,但是引入跨地域時延之後串行和并行的差別就會非常明顯,是以對這部分依賴需要做并發改造。另一方面在改造過程中梳理出核心依賴&非核心依賴,核心依賴強制要求單元化,對于非核心依賴做到并發&可觀測&可降級。
  • 緩存改造。由于以前對緩存的使用不夠嚴謹,會導緻單地域部署下被掩蓋的問題在多地域部署之下暴露出來。比如下面這種場景,在某個場景寫入某個key,然後在另一個場景下讀取這個key。在單地域部署下不會有問題,但是一旦多地域部署之後就有可能出現讀寫不同地域的情況導緻資料不一緻。
閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結

這種情況下我們需要:a)強制寫中心主節點。b)開通主節點到其他地域的資料同步。總的來說緩存改造兩大原則

  • 如果是非持久化緩存,則不用做任何改造。因為這種場景緩存不命中會有資料加載過程。但是很多非持久化緩存場景濫用了持久化緩存,針對這類case需要規範使用,改造成非持久化緩存。
  • 如果是持久化緩存,分為兩種情況:a)強一緻性,如分布式鎖,這種情況強制讀寫中心主叢集。b)非強一緻性,則強制寫中心主節點,就近讀。

導購鍊路涉及到很多異構系統,包括各個子領域應用構成的微服務叢集,以及衆多搜尋&推薦服務。異構主要展現在:a)編寫語言以及部署&運維平台的差異。b)服務注冊發現機制不一樣,主要包括configserver/vipserver/zookeeper。是以主要改造内容在于規範對這些元件的使用,調整流量路由政策保證流量區域内自閉環。

為了防止外部流量對閑魚導購流量的影響,我們在統一接入層加了一條流量糾偏政策:對于外部非導購鍊路的流量,強制切回中心區域。這一點非常重要,因為對于部署範圍之外的服務,如果因為這個原因導緻流量到了其他可用區域,其傳回資料的正确性我們沒法做保證。

服務叢集部署方案

閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結

微服務叢集整體采用對等部署。微服務叢集按照服務發現&注冊機制的不同劃分成三類:

  • 采用HSF作為RPC架構的業務服務,采用configserver做服務發現,configserver同時在多地域部署,彼此之間互相隔離,各地域部署的服務隻拉取本地域内的configserver資料,通過這種方式實作地域之間的流量隔離。但是中心區域的資料會同步至其他區域(區域挂了流量可以路由到中心區域,保證服務可用)。
  • 采用HTTP調用的算法服務叢集由于曆史&異構原因,采用了兩種服務注冊&發現方式
    • Zookeeper。Zookeeper在中心和區域都做單獨部署,用戶端請求的時候按照地域拉取對應的Zookeeper。通過這種方式實作流量的同機房通路,地域彼此間資料隔離,當單個地域服務出現問題時,隻能通過将其他區域的服務資料挂載到故障地域對應的Zookeeper下面來進行恢複。
    • vipserver(阿裡自研的一套叢集路由軟體負載均衡系統)。由于vipserver本身是分布式的負載均衡系統,且支援多種路由方式,故隻部署一套。

導購鍊路使用緩存的地方很多,大緻分成兩種用法

  • 緩解持久層的通路壓力。先通路緩存,緩存如果沒有資料則請求持久化層并把資料加載至緩存中,緩存本身不做資料一緻性保證。這種情況比較好處理,因為不涉及到多區域之間的同步,隻需要簡單做多地域部署即可。
閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結
  • 用作資料持久化。典型的如分布式鎖,計數器等。這種場景會有中心和區域的概念,彼此間雙向同步,這種場景在單區域部署的時候和上面的用法沒有太大差別,但是在多地域部署架構下,就會因為雙寫導緻資料出現不一緻,是以需要保證同一個key同一時間不能在多區域同時寫。
    • 區域同步至中心。因為資料需要做持久化,是以會在中心有一份完整的資料集,區域保證資料的最終一緻性即可。
    • 中心同步至區域。保證區域的資料和中心的資料一緻。
閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結

資料庫部署

按照分布式系統的CAP定理:Consistency(一緻性)、 Availability(可用性)、Partition tolerance(分區容錯性),三者不可得兼。是以嚴格意義上來說,資料庫的異地部署隻能三選二。但是在分布式系統中必然是分區的,而且分區之間的網絡我們沒法控制,也就是說P是一個事實,我們隻能從C和A中二選一,這分别對應着資料庫的兩種資料複制方式。

  • 主從複制模式的MySql:中心寫成功即傳回,從節點依賴主從之間的資料同步。這種模式下保證了A和P,犧牲了C。
閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結
  • 雙向複制模式的MySql:沒有主從節點之分,節點與節點之間實作資料最終一緻性。這種模式下同樣保證了A和P,犧牲了C。
閑魚異地多活架構設計與實作背景常用高可用架構多地部署帶來的變化多地域部署方案總結
  • 采用Paxos協定的分布式資料庫如Google的Spanner等,采用Paxos協定來保證資料的強一緻性,但是在Master節點挂了之後在新的Master選舉出來之前不可用,即保證了C和P,犧牲了A。

一方面根據導購鍊路的特點(絕大部分都是資料讀取操作,可以容忍短時間内的不一緻)。另一方面原有的資料存儲采用MySql,考慮到成本,最終選擇主從複制模式MySql。

總結

異地部署給系統帶來的最大挑戰是實體距離帶來的網絡延時,整個系統設計都圍繞着這個展開。總的來說在解決跨地域延時過程中我們遵循兩個大的原則:a)流量地域内自閉環。b)堅持可用性優先。在這兩個大原則之下從接入層,服務層以及資料存儲層做了相應的改造&部署。

目前閑魚部分鍊路已經實作了兩地三機房部署,并且已經承接線上流量,具備了異地容災的能力。同時經過本次改造,導購鍊路具備了較好的擴充性,能夠以極低的成本快速部署至更多機房。

但是一方面由于導購鍊路大部分都是隻讀場景,對資料要求弱一緻性即可。對于資料強一緻性場景帶給系統的挑戰會更大。另一方面業務是一個不停演進的過程,如何保證在演進過程中仍然能保證異地多活的部署架構,這是急需解決的問題。