閒魚異地多活架構設計與實現

背景

首頁和搜索一直以來都是閒魚導購的主陣地,爲了保證高可用業務上做了很多保護方案。但是隨着原有地域的 IDC 日漸趨於飽和,一些更深層次的問題開始暴露出來:

a)架構不具備擴展性。當服務量增大,單個 IDC 由於服務器部署、電力等物理因素無法滿足訴求,不能簡單的通過 IDC 部署來應對新增的流量。以算法爲例,算法在現有 IDC 資源飽和的情況下,其上新模型之前不得不等老模型下線,嚴重製約業務的迭代效率。

b)容災問題。當現有 IDC 出現故障時,如何保證導購主鏈路依然高可用。

常用高可用架構

• 同城雙活:系統從接入層以下在同城兩個機房做部署,這樣可以應對斷電,斷網等單機房故障。由於同城機房距離足夠近,可以近似看成一個機房,因此在部署上和單機房相比沒有特殊要求。但是遇到同區域災害時,服務就會受到影響,而且擴展性較差。

• 異地災備:系統從接入層以下除了在同城兩個機房做部署之外,在其他區域部署異地備份,底層數據根據實際要求做熱備 / 冷備,但是不承擔任何流量。當區域掛掉時,服務切至備份區域保證服務可用性。但異地災備的問題是:a)另一個區域不跑流量,出了問題不敢切。b)備份全站,資源利用率低。c)存在跨地域訪問。

• 異地多活:異地多活從接入層開始做多區域多機房部署,各個區域之間沒有主備的概念,均承擔相應的流量。它的優勢在於資源利用率高,擴展性較好。

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

多地部署帶來的變化

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

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

多地域部署方案

面臨的挑戰

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

用戶數據是否做拆分

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

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

但是前面提到閒魚導購鏈路特點是:a)可以容忍短時間的數據不一致。b)不涉及到數據庫的寫操作。顯而易見數據拆分顯得並沒有那麼必要,因此我們決定不做數據拆分。

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

流量路由方案

流量路由方案這裏麪包含了兩個問題:

如前面數據拆分部分提到,流量分發理論上需要和數據拆分邏輯保持一致。由於閒魚底層沒有做數據拆分,因此流量分發原則相對較爲靈活。

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

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

全鏈路升級改造

  1. 應用代碼改造。導購鏈路所有的依賴是否都能做多地部署,如果沒法多地部署跨地域時延是否會被放大。

  2. 服務之間的流量路由策略。導購鏈路涉及到很多異構的子系統,這些異構系統之間的流量是否遵循同地域優先,當某個地域服務掛了之後流量是否允許自動切到其餘地域。

  3. 流量強糾偏。導購的請求鏈路較爲複雜,會依賴衆多異構的子系統。雖然域名解析時流量會路由至對應的區域,但是在後續鏈路仍然有可能發生流量竄到其餘地域的情況,這種情況下理論上會對用戶體驗造成影響,所以在導購鏈路的每一跳節點都應該有糾偏策略。

  4. 外部流量由於分發策略我們沒法管控,會導致預期之外的流量流入。爲了避免這種情況,我們也需要有一個流量糾偏的策略。

導購鏈路涉及到很多異構系統,包括各個子領域應用構成的微服務集羣,以及衆多搜索 & 推薦服務。異構主要體現在:a)編寫語言以及部署 & 運維平臺的差異。b)服務註冊發現機制不一樣,主要包括 configserver/vipserver/zookeeper。因此主要改造內容在於規範對這些組件的使用,調整流量路由策略保證流量區域內自閉環。

爲了防止外部流量對閒魚導購流量的影響,我們在統一接入層加了一條流量糾偏策略:對於外部非導購鏈路的流量,強制切回中心區域。這一點非常重要,因爲對於部署範圍之外的服務,如果因爲這個原因導致流量到了其他可用區域,其返回數據的正確性我們沒法做保證。

服務集羣部署方案

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

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

數據庫部署

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

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

總結

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

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

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

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/dyvggvqDUpp-bdGjRtIUUQ