異地多活 (圖解+秒懂):不用 異地多活 , 你們 項目 怎么實現 高可用呢?
本文 的 原文 地址
原始的內容,請參考 本文 的 原文 地址
尼恩說在前面
在40歲老架構師 尼恩的讀者交流群(50+)中,尼恩一直在指導大家改造簡歷、指導面試。指導很多小伙伴拿到了一線互聯網企業網易、美團、字節、如阿里、滴滴、極兔、有贊、希音、百度、美團的面試資格,拿到大廠offer。
剛好前面幾天,有小伙伴面試,遇到一些異地多活的很重要的面試題:
高可用 異地多活,如何實現?
高可用 異地容災,你們 的 線上的方案是啥?
沒有 異地多活 , 你們 項目 怎么實現 高可用呢?
前段時間小伙伴面試美團,遇到這個問題。 小伙伴沒有準備好, 面試掛了。
在這里,尼恩給自己的技術自由圈(未來 超級 架構師) 社區的小伙伴, 積累一些 異地多活的架構方案和素材。這些資料的主要的目標:方便在架構指導的時候,作為參考資料。
在尼恩 之前有過多篇大廠異地多活的方案文章:
《美團面試:ES+Redis+MySQL高可用,如何實現?》
一、 高可用架構演進與異地多活
(1)什么是 高可用?
高可用性的核心在于保障系統服務的持續可用性。
可用性(Availability)是衡量架構設計的關鍵指標之一,通常結合高性能與可擴展性共同構成對核心架構的要求。
可用性(Availability) 公式如下:
可用性 (Availability) = MTBF / (MTBF + MTTR) * 100%
其中,兩個關鍵指標量化:
- 平均故障間隔 (MTBF - Mean Time Between Failure):系統正常運行的持續時長平均值。
- 故障恢復時間 (MTTR - Mean Time To Repair):系統從故障中恢復所需的時間平均值。
高可用性追求的核心是:最大限度地降低MTTR(快速恢復),使故障對用戶的影響最小化。
可用性 (Availability) 結果常以“N個9”表示(如99.9%、99.99%)。

各種規模的風險(硬件故障、軟件缺陷、自然災害)都可能引發故障。
高可用架構大致的演進路徑如下:一般從單一機房開始,逐步發展到同城雙機房,再到同城的多個機房。

隨著業務的快速發展以及部署需求的調整,進一步演進到異地多活模式。
異地多活部署模式 已經是標配
尼恩一直以來都是輔導小伙伴 改造簡歷,輔導面試,輔導1000多人了。
可以說, 改遍了 各大廠的簡歷, 各個大廠的項目, 異地多活部署模式 已經是標配 。
異地多活 (Multi-Site High Availability, MSHA) 架構 正是為了應對大規模故障(尤其是機房或城市級災難),實現快速恢復和業務連續性的有效方案。

(2)單機架構與風險
業務初期,系統架構通常十分簡單:
應用服務器與單實例數據庫(如MySQL)部署在同一環境中。

核心風險:單點故障。
一旦數據庫服務器因磁盤損壞、操作系統錯誤或誤操作出現故障,所有數據將丟失。
應對措施1 - 冗余備份:
- 定期將數據庫文件復制到另一臺機器。

缺陷:
- 恢復時間長: 需停機恢復數據,時長取決于數據量大小。
- 數據不完整: 備份周期內的新數據會丟失。
此方案難以滿足基本可用性要求。
應對措施2 - DB 的主從副本架構
引入冗余機制是解決 DB 單點 故障的關鍵。
對數據庫層,可以采用主從復制 (Master-Slave Replication):

優勢:
- 數據完整性高: 主從副本(接近)實時同步,數據差異小。
- 容錯能力強: 主庫故障時,可手動或自動將從庫提升(Promote)為主庫接管服務。
- 讀性能提升: 讀請求可分流至從庫,減輕主庫壓力。
應對措施2 - 多點部署 以避免 單點故障
由于業務應用通常是無狀態的,亦可部署多個實例以避免單點故障。
此時需引入負載均衡器(如 Nginx/LVS):

此架構成為應對機器級故障的主流模式。
(3)如何 實現 機房級別的容災?
體量很小的系統,它會重點關注「用戶」規模、增長。
這個階段, 獲取用戶是一切。
等用戶體量上來了,這個階段會重點關注「性能」。
這個階段, 優化接口響應時間、頁面打開速度等等,這個階段更多是關注用戶體驗。
等體量再大到一定規模后你會發現,「可用性」就變得尤為重要。
像微信、支付寶這種全民級的應用,如果機房發生一次故障,那整個影響范圍可以說是非常巨大的。
比如經??吹竭@樣的案例:
- 2015 年 5 月 27 日,杭州市某地光纖被挖斷,近 3 億用戶長達 5 小時無法訪問支付寶
- 2021 年 7 月 13 日,B 站部分服務器機房發生故障,造成整站持續 3 個小時無法訪問
- 2021 年 10 月 9 日,富途證券服務器機房發生電力閃斷故障,造成用戶 2 個小時無法登陸、交易
- ...
那到底該怎么應對機房級別的故障呢?
沒錯,思想還是冗余。
只不過這次要冗余機房
(4)應對機房風險:同城災備
即使服務器分散在多個機柜,它們仍依賴同一個機房的基礎設施(電力、網絡、溫控等)。
機房級別的故障雖然概率低,但影響巨大(如光纖挖斷、電力故障、自然災害),案例時有發生。
系統規模越大,此類風險的影響就越不可忽視。
核心思路:機房級冗余 - 同城災備
在同一城市的不同區域建立第二個機房(災備機房),通過專線與主機房連通。

這里有兩種方案:冷備和熱備
冷備VS熱備:
| 特性 | 冷備 (Cold Backup) | 熱備 (Hot Backup) |
|---|---|---|
| 定義 | 在系統停機狀態下備份數據 | 在系統運行期間實時備份數據 |
| 數據狀態 | 備份時間點前的靜態數據 | 實時動態數據(含最新操作) |
| 恢復時間(RTO) | 小時級 (需手動恢復) | 分鐘級 (自動切換) |
| 數據丟失(RPO) | 上次備份至故障時間的數據丟失 | 秒級丟失(或零丟失) |
| 資源占用 | 低(僅存儲成本) | 高(需冗余服務器+實時同步) |
| 業務影響 | 需停止服務 | 服務無中斷 |
| 技術復雜度 | 簡單(定時腳本備份) | 復雜(需雙活架構+心跳檢測) |
| 典型場景 | 歷史數據歸檔、合規性備份 | 核心交易系統、實時業務 |
| 成本 | 低(存儲硬件為主) | 高(服務器+網絡+軟件許可) |
(1)冷備方案: 僅在災備機房做數據定時備份。
- 缺點: 與單機備份相同——數據恢復慢、丟失。

(2)熱備方案: 在災備機房部署:
- 數據庫從庫:實時復制主庫數據。
- 業務應用:提前部署好。
- 負載均衡器:提前配置好。
- 優點: 當主生產機房故障時,只需:
- 提升災備機房的從庫為主庫。
- 將流量(通過DNS或其他服務發現)切換到災備機房的負載均衡器。
- 業務恢復(MTTR大大縮短)。

總結:
同城災備(冷備或熱備)解決了機房級故障時的數據安全和服務恢復速度問題。
熱備方案通過“預部署”進一步降低了MTTR。
但災備機房平時不承擔主要生產流量,資源利用率低。
(5)效能優化:同城雙活
從「成本」的角度來看,我們新部署一個機房,需要購買服務器、內存、硬盤、帶寬資源,花費成本也是非常高昂的,只讓它當一個后備軍,未免也太「大材小用」了!
如何 最大化 備機房 的價值?
如何 提升 備機房 的利用率低?
為了解決災備機房利用率低的問題,并增強災備系統的實戰可靠性,讓災備機房也接入真實流量,如下圖

但這里有一個問題:
B 機房的存儲,現在可都是 A 機房的「從庫」,從庫默認可都是「不可寫」的,B 機房的寫請求打到本機房存儲上,肯定會報錯,這還是不符合我們預期。
怎么辦?
這時,你就需要在「業務應用」層做改造了。
你的業務應用在操作數據庫時,需要區分「讀寫分離」(一般用中間件實現),即兩個機房的「讀」流量,可以讀任意機房的存儲,但「寫」流量,只允許寫 A 機房,因為主庫在 A 機房。

關鍵總結:
(1)讀寫分離: 應用層需要區分讀寫操作。所有寫操作必須指向主庫(在主生產機房1)。讀操作可根據規則指向本機房的從庫或其他可讀節點。
(2)運作模式: 兩個機房均處理業務流量(主要是讀,但寫統一到主機房),邏輯上被視為一個整體。任何一機房故障,另一機房可接管其全部流量。
(3)優點:
- 資源利用率提升: 災備機房參與服務,分擔壓力。
- 災備能力增強: 災備機房一直在“實戰”狀態,切換更可靠。
- 維護靈活性: 流量可在機房間切換,便于維護升級。
(4)缺點
- 業務層需要改造讀寫分離模式
- 雖然提供了機房級別的容災能力。然而,其致命弱點是無法抵御城市級災難(如地震、洪水)
更高性能的方案是, 雙主同步的改造:
可以不同的機房的DB,做成雙主模式,實現本地寫入、異地同步。
(6)如何 實現 城市級容災?
為解決城市級災難風險,需跨地域部署機房。
常見方案是在另一個相距較遠的城市(通常建議1000公里以上)部署第三個機房。
這就是 兩地三中心。


2個同城機房構成雙活/主從集群,異地機房只做數據冷備或異步熱備(不接入流量)。
這就稱為“兩地三中心”
三中心的主要用于,備份和災難后恢復,旨在防止主城市毀滅性災難導致的數據丟失。
缺點:
-
異地機房啟用時間長,流程復雜。
-
啟用后服務狀態不確定(未經實戰)。
-
資源利用率低(基本為后備)。
此方案是銀行、金融系統常用的容災模式,但互聯網追求更高可用性的場景,會進一步向異地多活演進。
(7)效能優化:異地多活
從「成本」的角度來看,我們新部署一個機房,需要購買服務器、內存、硬盤、帶寬資源,花費成本也是非常高昂的,只讓它當一個后備軍,未免也太「大材小用」了!
如何 最大化 備機房 的價值?
如何 提升 備機房 的利用率低?
辦法是: 異地多活
異地雙活旨在兩個異地(城市)的機房都實時提供服務,并能在任一城市發生故障時,由另一城市迅速接管全部流量。
比如字節的異地多活架構規劃如下:

(7)1 異地多活 主要挑戰:網絡延遲
物理距離(如北京到上海1300公里)帶來顯著的網絡延遲(通常專線RTT 30ms~100ms以上)。
相比機房內網絡延遲(<1ms)和同城延遲(約1~3ms),跨城延遲相差數十倍甚至百倍以上。
直接照搬同城雙活(即一個城市的應用讀寫另一個城市的數據庫)會導致接口響應顯著延遲(可能達到秒級),用戶體驗無法接受。
| 操作/延遲來源 | 延遲時間 | 對比倍數 (相較于機房內網絡) | 影響說明 |
|---|---|---|---|
| 跨城網絡延遲 (北京-上海) | 30 ms - 100 ms+ | 30x - 100x+ | 跨機房服務/數據庫調用單次往返延遲顯著增加。 |
| 同城網絡延遲 (同城市內) | 1 ms - 3 ms | 2x - 6x | 服務/數據庫跨機房調用延遲有一定增加,但可接受。 |
| 機房內網絡延遲 | < 1 ms (通常0.1-0.5ms) | 1x | 服務間或服務到本地數據庫延遲極低。 |
核心問題:跨機房服務/數據庫調用因延遲過高無法接受。
以下來自于餓了么數據
- 異地(北京到上海)30ms
- 同城(上海2個機房之間)1ms
- 內網0.5ms
異地多活面臨的主要挑戰是網絡延遲,以北京到上海 1468 公里,即使是光速傳輸,一個來回也需要接近10ms,我們在實際測試的過程中,發現上海到北京的網絡延遲,一般是 30 ms。
這 30 ms可以和運算系統中其他的延遲時間做個比較:
L1 cache reference ......................... 0.5 ns
Branch mispredict ............................ 5 ns
L2 cache reference ........................... 7 ns
Mutex lock/unlock ........................... 25 ns
Main memory reference ...................... 100 ns
Compress 1K bytes with Zippy ............. 3,000 ns = 3 μs
Send 2K bytes over 1 Gbps network ....... 20,000 ns = 20 μs
SSD random read ........................ 150,000 ns = 150 μs
Read 1 MB sequentially from memory ..... 250,000 ns = 250 μs
Round trip within same datacenter ...... 500,000 ns = 0.5 ms
Read 1 MB sequentially from SSD* ..... 1,000,000 ns = 1 ms
上海之間兩個機房的網絡延時................. 1,000,000 ns = 1 ms
Disk seek ........................... 10,000,000 ns = 10 ms
Read 1 MB sequentially from disk .... 20,000,000 ns = 20 ms
北京到上海的網絡延時.................... 30,000,000 ns = 30 ms
Send packet CA->Netherlands->CA .... 150,000,000 ns = 150 ms
北京上海兩地的網絡延遲時間,大致是內網網絡訪問速度的 60 倍(30ms/0.5ms),如果不做任何改造,一方直接訪問另外一方的服務,那么我們的APP的反應會比原來慢 60 倍,其實考慮上多次往返,可能會慢600倍。
如果機房都在上海,那么網絡延遲只有內網速度的2倍,可以當成一個機房使用。
所有有些公司的多活方案,會選擇同城機房,把同城的幾個機房當成一個機房部署,可以在不影響服務架構的情況下擴展出多個機房,不失為一個快速見效的方法。
與同城多活的方案不同,異地多活的方案會限制機房間的相互調用,需要定義清晰的服務邊界,減少相互依賴,讓每個機房都成為獨立的單元,不依賴于其他機房。
同城多活 和 異地多活的方案 對比:

關鍵結論:異地RT可能慢百倍(30ms/0.5ms = 60倍),考慮多次往返可能600倍
(7)2 解決方案:單元化架構
避免高延遲的關鍵在于避免跨機房調用。
需保證一個用戶的請求鏈路上所有操作(包括讀寫數據庫)在一個機房內完成(“業務閉環”)。
具體方案:單元化架構(Cell-based Architecture)。

單元化架構 核心思想:
(1)路由分片 (Routing/Sharding): 在流量入口層(路由層),根據預設規則將特定用戶(或業務范圍)的請求固定路由到指定城市的機房(單元)。
(2)常見分片規則:
- 用戶屬性分片 (如 UserID Hash): 簡單有效,保證同一用戶總在同一單元。適用于普通C端業務(如電商)。
- 地理分片 (Geographic Sharding): 根據用戶地理位置將請求路由到最近的單元。天然適合LBS業務(如外賣、打車)。
- 業務分片: 按業務線維度劃分單元(如應用A在北京,應用B在上海)。
(3)多機房主庫 (Multi-Master): 每個機房的存儲層(DB, Cache, MQ)都是主庫/主節點。服務讀寫本機房存儲。
(4)數據雙向同步: 每個機房寫入的數據,通過數據同步中間件(如Canal+MQ、自研同步工具)可靠地雙向同步到其他機房,最終保證各機房擁有全量數據。
(5)單元內閉環: 進入北京單元用戶的請求,只與北京的DB/Cache/MQ交互。進入上海單元用戶的請求,只與上海的DB/Cache/MQ交互。避免跨單元遠程調用。
(6)容災切換: 任一城市故障時,路由層將故障單元的所有用戶流量整體切換到健康單元。該單元已有全量數據和所有服務部署,可立即接管。
字節的單元化異地多活架構基本方案:

(7)3 單元化架構的分片路由
在異地雙活架構中,相較于復雜的數據合并與沖突處理方案,更為有效的策略是從流量入口源頭避免數據沖突的發生。
這種做法的核心在于: 確保同一個用戶的所有操作都在同一單元內完成,從而杜絕跨單元寫操作。
實現這一目標的關鍵是采用分片路由(Sharded Routing)的流量分發機制:

下面分析常見分片策略:
(7)3.1 業務類型分片(服務級隔離)
實現原理: 按應用服務劃分單元邊界
示例場景:
- 北京單元承載訂單服務和支付服務
- 上海單元承載用戶社區和內容服務

關鍵優勢:
- 天然避免跨單元調用:關聯服務部署在同一單元
- 維護清晰的服務邊界
- 適用業務:功能模塊邊界清晰的中臺系統
注意問題: 需確保業務單元內部服務的完整閉環,避免因功能耦合導致跨單元調用。
(7)3.2 用戶哈希分片(用戶級隔離)
實現原理: 對用戶ID哈希取模映射單元
示例場景:
- 用戶ID 1-1000 分配至北京單元
- 用戶ID 1001-2000 分配至上海單元
架構示意:

核心價值:
- 保證同一用戶請求始終路由至相同單元
- 用戶數據變更自然隔離
- 適用場景:用戶數據獨立性強的業務(如社交平臺、電商系統)
技術實現: 哈希算法通常結合一致性哈希,支持單元彈性擴展時最小化路由變動。
(7)3.3 地理分片(區域級隔離)
實現原理: 按用戶地理位置就近分配單元
示例場景:
- 華北用戶路由至北京單元
- 華東用戶路由至上海單元
架構示意:

獨特優勢:
- 天然符合地理位置敏感型業務特征
- 結合網絡拓撲實現最低訪問延遲
- 適用業務:本地生活服務(外賣、打車)、區域化業務
實施要點: 需建設精準的IP地理定位庫,同時考慮用戶跨區域移動時的會話保持。
(7)4 關鍵技術與難點
(1)數據同步中間件: 開發高性能、高可靠、支持斷點續傳、沖突檢測/處理的數據同步組件是關鍵(如阿里Canal、自研工具)。需支持多種存儲(MySQL, Redis, Kafka等)。
(2)數據沖突處理:
- 被動策略 (Conflict Detection & Resolution): 中間件檢測寫入沖突(短時間內同一數據在兩個單元被修改),通常以時間戳(需可靠時鐘)或版本號裁定最新值。實現復雜,依賴全局時鐘一致性。
- 主動策略:從源頭避免 (Avoidance): 通過路由規則(Sharding Key)確保對特定數據的修改只發生在同一單元內(如同一個UserID或地理圍欄內的騎手ID始終在同一單元路由)。這是推薦的主要方式。
(3)“數據歸屬”兜底: 為防止路由規則失效或Bug導致數據被跨單元修改,服務層或中間件需做校驗,攔截“不合規”的跨單元數據操作。這是保障安全性的重要兜底措施。
(4)全局數據 (Global Data) 處理: 如全局配置、庫存(要求強一致),無法分片路由。通常處理方式:
-
僅在一個“主中心單元”部署寫服務(遵循寫本單元原則)。
-
其他單元讀請求可本地讀(若有從庫)或代理到主中心單元(犧牲延遲換取強一致)。
-
或采用分布式強一致協議(如Paxos/Raft),但延遲可能更高。
-
重點:優先保證核心業務雙活,全局數據服務按需實現,不一定強求異地雙活。
-
依賴治理: 明確單元邊界,梳理服務間依賴,避免跨單元RPC調用。
(5)異地多活架構要解決的關鍵問題:
-
區域劃分:首先要選擇一個劃分方法(Sharding Key),對服務進行分區,讓服務內聚到同一個 ezone 中。分區方案是整個多活的基礎,它決定了之后的所有邏輯。
- 比如淘寶使用用戶ID取模,來劃分單元
- 餓了么使用騎手,商家,用戶的地理位置(地理圍欄)劃分區域單元
-
數據復制:為了實現可用優先原則,所有機房都會有全量數據,這樣用戶可以隨時切換到其他機房,全量數據就需要對數據進行實時復制,使用相應的中間件,對 mysql,zookeeper ,消息隊列和 redis 的數據進行復制。
-
區域優先原則:因為做了異地多活,會發現多個區域的服務,數據(mysql,reids)等。因為跨區域訪問,會慢600倍(餓了么的數據),所以要保證優先訪問當前區域的服務,數據。
-
故障轉移原則:如果當前區域的服務、數據(mysql,redis)不能訪問,能夠進行故障轉移,來訪問其他zone的的服務和數據。
二、案例:餓了么異地多活架構實踐
(1)業務分析
餓了么業務核心流程包含三個關鍵角色交互:

業務過程中包含3個最重要的角色,分別是用戶、商家和騎手,一個訂單包含3個步驟:
(1) 用戶打開我們的APP,系統會推薦出用戶位置附近的各種美食,推薦順序中結合了用戶習慣,推薦排序,商戶的推廣等。用戶找到中意的食物 ,下單并支付,訂單會流轉到商家。
(2) 商家接單并開始制作食物,制作完成后,系統調度騎手趕到店面,取走食物
(3) 騎手按照配送地址,把食物送到客戶手中。
核心特征:
(1) 強地域性:用戶、商家、騎手需在相同地理區域
(2) 實時性要求:訂單全流程需在30分鐘內完成
(3) 數據一致性挑戰:三方狀態實時同步
(2)架構設計原則

- 業務內聚:每個訂單的全流程(用戶下單、商家接單、騎手配送)必須在同一個機房(稱為“ezone”)內完成,不允許跨機房調用。這是為了避免跨城網絡延遲影響實時性——同一ezone內的用戶、商家、騎手數據本地化,訂單流轉速度最快。
- 可用性優先:故障切換時,優先保證用戶能下單吃飯,允許短期數據不一致(事后修復)。每個ezone存儲全量業務數據,某一ezone故障后,其他ezone可無縫接管用戶。
- 數據正確性:切換過程中若發現訂單狀態不一致,會鎖定該訂單阻止修改,避免錯誤擴散,確保核心業務(如支付、配送)數據正確。
- 業務可感:基礎設施無法完全屏蔽跨機房差異,需要業務代碼配合——比如識別數據歸屬地、過濾非本ezone數據、通過狀態機糾正不一致。
(3)分片策略:地理圍欄劃分
為了實現業務內聚,我們首先要選擇一個劃分方法(Sharding Key),對服務進行分區,讓用戶,商戶,騎手能夠正確的內聚到同一個 ezone 中。分區方案是整個多活的基礎,它決定了之后的所有邏輯。
根據餓了么的業務特點,我們自然的選擇地理位置作為劃分業務的單元,把地理位置上接近的用戶,商戶,騎手劃分到同一個ezone,這樣一個訂單的履單流程就會在一個機房完成,能夠保證最小的延時,在某個機房出現問題的時候,也可以按照地理位置把用戶,商戶,騎手打包遷移到別的機房即可。
所以我們最終選擇的方案如下圖,自定義地理劃分圍欄,用圍欄把全國分為多個 shard,圍欄的邊界盡量按照行政省界,必要的時候做一些調整,避免圍欄穿過市區。一個ezone可以包含多個 shard,某個 ezone 的 shard ,可以隨時切換到另外一個 ezone ,靈活的調度資源和failover。

這樣的劃分方案,基本解決了垮城市下單的問題,線上沒有觀察到有跨 ezone 下單的情況。圍欄的劃分是靈活的,可以隨著以后業務的拓展進行修改,因為每個機房都是全量數據,所以調整圍欄不會導致問題。
對這種劃分方法,有一些常見的疑問,比如:
1.如果兩個城市是接壤的,會出現商家和用戶處于不同 ezone 的情況,豈不是破壞了內聚性原則?

這種情況確實會出現,為了盡量避免,我們在劃分shard的時候沒有簡單的用城市名稱,而是用了復雜的地理圍欄實現,地理圍欄主體按照省界劃分,再加上局部微調,我們最大限度的避免了跨ezone下單的情況。但如果真的出現了,用戶下單也不受影響,最多只是狀態有1s左右的延遲。
2.用戶是會動的,如果用戶從北京到了上海,那么劃分規則應該怎么應對?
用戶在北京下單,數據落在北京shard,到上海下單,數據則落在上海的 shard,借助于底層的數據同步工具,用戶無論在什么地方,都能看到自己的數據,但是有1s左右的延時,對于大部分的業務場景,這個延遲是可以承受的。當然也有些業務場景不能接受這 1s 的延時,我們也提供了另外的方案來應對,參考下文介紹Globa Zone的章節。
3.為什么不簡單點,按照用戶的ID來切分?
阿里是按照用戶ID的取模來劃分單元的,比較簡潔。我們如果也用ID做切分,同一地方的用戶,商戶,騎手可能被劃分到不同 ezone,就會出現比較多的跨機房調用,這樣就更可能出現延遲,難以保證實時性。所以,我們本地配送的業務模式,決定了需要用地理位置來劃分服務。
(4)流量路由體系

基于地理位置劃分規則,我們開發了統一的流量路由層(API Router),這一層負責對客戶端過來的 API 調用進行路由,把流量導向到正確的 ezone。API Router 部署在多個公有云機房中,用戶就近接入到公有云的API Router,還可以提升接入質量。

前端 APP 做了改造,為每個請求都帶上了分流標簽,API Router 會檢查流量上自帶的分流標簽,把分流標簽轉換為對應的 Shard ID,再查詢 Shard ID 對應的 eZone,最終決定把流量路由到哪個 ezone。
最基礎的分流標簽是地理位置,有了地理位置,AR 就能計算出正確的 shard 歸屬。但業務是很復雜的,并不是所有的調用都能直接關聯到某個地理位置上,我們使用了一種分層的路由方案,核心的路由邏輯是地理位置,但是也支持其他的一些 High Level Sharding Key,這些 Sharding Key 由 APIRouter 轉換為核心的 Sharding Key,具體如下圖。這樣既減少了業務的改造工作量,也可以擴展出更多的分區方法。

除了入口處的路由,我們還開發了 SOA Proxy,用于路由SOA調用的,和API Router基于相同的路由規則。APIRouter 和 SOAProxy 構成了流量的路由通道,讓我們可以靈活的控制各種調用在多活環境下的走向。
(5)數據同步機制
為了實現可用優先原則,所有機房都會有全量數據,這樣用戶可以隨時切換到其他機房,全量數據就需要對數據進行實時復制,餓了么開發了相應的中間件,對 mysql,zookeeper ,消息隊列和 redis 的數據進行復制。

(1)Mysql 數據復制工具 DRC:
Mysql 的數據量最大,每個機房產生的數據,都通過 DRC 復制到其他 ezone,每個ezone的主鍵取值空間是ezoneid + 固定步長,所以產生的 id 各不相同,數據復制到一起后不會發生主鍵沖突。
按照分區規則,正常情況下,每個 ezone 只會寫入自己的數據,但萬一出現異常,2個 ezone 同時更新了同一筆數據,就會產生沖突。DRC 支持基于時間戳的沖突解決方案,當一筆數據在兩個機房同時被修改時,最后修改的數據會被保留,老的數據會被覆蓋。
(2)ZooKeeper 復制:
有些全局的配置信息,需要在所有機房都完全一致,餓了么開發了 zookeeper 復制工具,用于在多個機房中同步 ZK 信息。
(3)消息隊列和Redis復制:
MQ,Redis 的復制與 ZK 復制類似,也開發了 相應的復制工具。
強一致保證:對于個別一致性要求很高的應用,餓了么提供了一種強一致的方案(Global Zone),Globa Zone是一種跨機房的讀寫分離機制,所有的寫操作被定向到一個 Master 機房進行,以保證一致性,讀操作可以在每個機房的 Slave庫執行,也可以 bind 到 Master 機房進行,這一切都基于我們的數據庫訪問層(DAL)完成,業務基本無感知。

(6)整體結構
以上介紹了各個考慮的方面,現在可以綜合起來看,餓了么多活的整體結構如下圖:

(7)常見問題
由于平臺 篇幅限制, 此處省略1000字+
原始的內容,請參考 本文 的 原文 地址
浙公網安備 33010602011771號