彩虹橋架構(gòu)演進(jìn)之路-負(fù)載均衡篇
一、前言
一年一更的彩虹橋系列又來了,在前面兩期我們分享了在穩(wěn)定性和性能2個層面的一些演進(jìn)&優(yōu)化思路。近期我們針對彩虹橋 Proxy 負(fù)載均衡層面的架構(gòu)做了一次升級,目前新架構(gòu)已經(jīng)部署完成,生產(chǎn)環(huán)境正在逐步升級中,借此機(jī)會更新一下彩虹橋架構(gòu)演進(jìn)之路系列的第三篇。
二、背景
彩虹橋目前依賴 SLB 做負(fù)載均衡和節(jié)點(diǎn)發(fā)現(xiàn),隨著業(yè)務(wù)發(fā)展流量越來越高,SLB 帶寬瓶頸逐漸暴露,雖然在半年前做過一次雙 SLB 改造臨時解決了帶寬瓶頸,但運(yùn)維成本也隨之變高。除了帶寬瓶頸外,SLB 無法支持同區(qū)優(yōu)先訪問,導(dǎo)致難以適配雙活架構(gòu)。所以準(zhǔn)備去除彩虹橋?qū)?SLB 的強(qiáng)依賴,自建彩虹橋元數(shù)據(jù)中心,提供負(fù)載均衡和節(jié)點(diǎn)發(fā)現(xiàn)等能力,同時支持同區(qū)訪問等能力來更好的適配雙活架構(gòu)。下面會詳細(xì)介紹一下彩虹橋元數(shù)據(jù)中心以及 SDK 相關(guān)能力的相關(guān)細(xì)節(jié)。
三、核心名稱解釋
圖片
四、現(xiàn)有架構(gòu)回顧
在開始介紹彩虹橋元數(shù)據(jù)中心之前,我們先來回顧一下彩虹橋目前架構(gòu),以及存在的一些痛點(diǎn)。
現(xiàn)有架構(gòu)
圖片
- 業(yè)務(wù)服務(wù)集成 SDK 通過域名訪問,請求經(jīng)過 SLB 轉(zhuǎn)發(fā)到具體的 Proxy 節(jié)點(diǎn)。
- 每個集群掛載雙 SLB,SDK 通過 DNS 解析輪訓(xùn)路由到2個 SLB,2個 SLB 掛載不同的后端節(jié)點(diǎn)。
- 每個集群部署的 Proxy 節(jié)點(diǎn)均為一個可用區(qū),雙活架構(gòu)為集群維度多可用區(qū)部署。
- 業(yè)務(wù)側(cè)大多數(shù)為多可用區(qū)混布,單同一個邏輯庫只會連接一個彩虹橋集群,由于彩虹橋一個集群內(nèi)的節(jié)點(diǎn)均為同一可用區(qū),所以業(yè)務(wù)服務(wù)-彩虹橋這條鏈路必然會出現(xiàn)一半節(jié)點(diǎn)跨區(qū)訪問。
- 彩虹橋集群按照業(yè)務(wù)域劃分,彩虹橋集群所屬業(yè)務(wù)域的 RDS 大多數(shù)都會跟彩虹橋集群同區(qū)。比如彩虹橋交易集群為i區(qū),歸屬交易集群的邏輯庫掛載的 RDS 大多數(shù)也都是i區(qū)。
主要痛點(diǎn)
- SLB 帶寬已達(dá)瓶頸(5Gb/s,歷史上出現(xiàn)過多次 SLB 帶寬達(dá)到 100%的情況),目前彩虹橋單集群掛載了雙 SLB 暫時解決帶寬瓶頸但仍存在痛點(diǎn):1. SLB 擴(kuò)容流程較復(fù)雜(配置監(jiān)聽、配置虛擬服務(wù)器組、監(jiān)聽綁定虛擬服務(wù)組,配置調(diào)度算法、更新域名解析的等),基于目前發(fā)布系統(tǒng)能力無法實(shí)現(xiàn)全自動化。根據(jù)之前混沌工程演練結(jié)果,SLB 擴(kuò)容流程需要30分鐘左右。2. SLB 擴(kuò)容后,需要改域名解析,DNS 解析生效需要一段時間(域名 TTL 1 分鐘,本地緩存10分鐘),新 SLB 需要10分鐘左右才開始逐漸承載流量,無法實(shí)現(xiàn) SLB 快速擴(kuò)容。
- 單可用區(qū)故障時,需要人工操作切流到其他可用區(qū)集群,SLA 難以保證(目前無法自動化判定單可用區(qū)故障,且集群級別流量調(diào)度需要人工預(yù)估集群負(fù)載,難以實(shí)現(xiàn)自動化切流)。
- SLB 目前支持最低權(quán)重為1/100,粒度較粗,無法支撐發(fā)布過程中的更小流量灰度需求。
- Proxy 單個集群所有節(jié)點(diǎn)均為同一個 AZ,需要與下游 RDS 保證同 AZ,跨集群流量調(diào)度靈活性差,很難實(shí)現(xiàn)多可用區(qū)流量均衡(目前由于大部分 RDS 為 I 區(qū),Proxy 多可用區(qū)流量非常不均衡:i區(qū) 90%/k 區(qū)流量 10%)。
五、自建元數(shù)據(jù)中心&SDK 增強(qiáng)
圖片元數(shù)據(jù)中心獨(dú)立部署
- 新增 Metadata 數(shù)據(jù)庫,多可用區(qū)部署(需要跟集群中的 Proxy 同區(qū))。
- 新增 MetaCenter 服務(wù),多可用區(qū)部署。
- Proxy 連接所有 Metadata 數(shù)據(jù)庫,注冊&心跳都會寫入到所有數(shù)據(jù)庫。
- MetaCenter 服務(wù)定時查詢所有 metadata 數(shù)據(jù)庫,基于心跳版本號和多個數(shù)據(jù)庫的并集篩選出健康的節(jié)點(diǎn)列表存儲到內(nèi)存中。
- MetaCenter 服務(wù)提供 API,查詢 MetaCenter 內(nèi)存中的可用節(jié)點(diǎn)列表數(shù)據(jù)。
- SDK 啟動時會去通過7層 SLB 訪問 MetaCenter 提供的 API 拉取節(jié)點(diǎn)列表并存儲到內(nèi)存,運(yùn)行中每隔 5s 更新一次。
- MetaCenter 每次計算時如果有節(jié)點(diǎn)下線,通過 ARK 實(shí)時下發(fā)拉取事件給 SDK,SDK 會立刻重新拉取一次節(jié)點(diǎn)列表。
- SDK 通過下發(fā)的節(jié)點(diǎn)列表做負(fù)載均衡,優(yōu)先路由到同可用區(qū)的 Proxy 節(jié)點(diǎn),其次按照節(jié)點(diǎn)權(quán)重輪訓(xùn)。
- SDK 輪訓(xùn)間隔時間和節(jié)點(diǎn)變更事件下發(fā)開關(guān)均為可配置,實(shí)時生效。
架構(gòu)詳解
Metadata 數(shù)據(jù)庫
節(jié)點(diǎn)表結(jié)構(gòu)設(shè)計
- beat_version:心跳版本號,只有上報心跳時會更新。
- config_version:配置版本號,更新權(quán)重&狀態(tài)時會更新。
- enabled:是否啟用
Proxy
節(jié)點(diǎn)啟動時
- 注冊:啟動時會去所有 metadata 數(shù)據(jù)庫注冊當(dāng)前節(jié)點(diǎn),如果 node_info 不存在對應(yīng)節(jié)點(diǎn)記錄,則新增,如果存在則修改權(quán)重為初始權(quán)重。
- 啟動完成后需要調(diào)用 bifrost-admin 提供的調(diào)用節(jié)點(diǎn)啟用 API(發(fā)布腳本)
update node_info
set weight = 1, config_version = #{config_version}
where cluster_name = ? and address = ?
節(jié)點(diǎn)運(yùn)行時
- 心跳:定時更新所有 metadata 數(shù)據(jù)庫節(jié)點(diǎn)記錄的 beat_version 字段
update node_info set beat_version = beat_version + 1
where cluster_name = ? and address = ?
節(jié)點(diǎn)下線
- 調(diào)用 bifrost-admin 提供的下線 OPEN API(發(fā)布腳本)
MetaCenter( Heimdall)
- 啟動時
初始化心跳版本號:記錄所有 metadata 數(shù)據(jù)庫每個節(jié)點(diǎn)最新 beat_version 和初始化心跳丟失次數(shù)到內(nèi)存
圖片
圖片
- 運(yùn)行時
定時查詢節(jié)點(diǎn)信息(3s 一次),篩選可用節(jié)點(diǎn)并寫入到內(nèi)存中,提供 OpenAPI 給 SDK 調(diào)用,每個庫均執(zhí)行以下操作,最終會得到每個庫的可用節(jié)點(diǎn)列表,最后把多個 list 求并集,得到最終的可用列表,寫入到內(nèi)存中。
查出所有列表數(shù)據(jù)后,對比內(nèi)存中的 beat_version 與數(shù)據(jù)庫中的 beat_version,如不相同則更新內(nèi)存,如果相同說明對應(yīng)節(jié)點(diǎn)心跳有丟失,如果丟失次數(shù)超過閾值,則剔除此節(jié)點(diǎn)。
節(jié)點(diǎn)列表中除了 ip、端口信息外,還有權(quán)重,啟用狀態(tài)屬性, 這些屬性都屬于控制流變更,如果出現(xiàn)2邊數(shù)據(jù)庫不一致場景,以 config_version 最大的為準(zhǔn)。
1.2.3.20節(jié)點(diǎn)與K區(qū)網(wǎng)絡(luò)斷開
1.2.3.20節(jié)點(diǎn)宕機(jī)
如果本次計算時有節(jié)點(diǎn)列表變化,會下發(fā)一個變更事件到 ARK(value 為時間戳-秒),SDK 在收到次配置變更后會立刻到 MetaCenter 拉取一次節(jié)點(diǎn)列表,以彌補(bǔ)定時輪訓(xùn)的延時。
- 兜底配置
MetaCenter 提供的 OpenAPI 是通過計算后存入內(nèi)存的數(shù)據(jù),為了可以人工干預(yù)節(jié)點(diǎn)列表,需要支持開關(guān)一鍵切換至人工配置的節(jié)點(diǎn)列表數(shù)據(jù)。
圖片
SDK( Rainbow)
- SDK 啟動時會去通過7層 SLB 拉取節(jié)點(diǎn)列表并存儲到內(nèi)存,運(yùn)行中每隔5s更新一次。如果拉取失敗,啟動時報錯,運(yùn)行中不做任何處理,等待下次拉取。如果拉取的可用節(jié)點(diǎn)列表為空,啟動時報錯,運(yùn)行時兜底不做任何處理,等待下次拉取。
- 拉取的可用節(jié)點(diǎn)列表與內(nèi)存中做對比,如果有節(jié)點(diǎn)被移除,需要優(yōu)雅關(guān)閉對應(yīng)的存量連接(如果被移除節(jié)點(diǎn)超過1個,則不做驅(qū)逐)。 當(dāng)可用節(jié)點(diǎn)數(shù)量/所有節(jié)點(diǎn)數(shù)量 < X%時,忽略本次變更,不更新內(nèi)存中的可用節(jié)點(diǎn)列表。
- 拉取的節(jié)點(diǎn)數(shù)據(jù)會按照可用區(qū)進(jìn)行分組,分為同可用區(qū)&跨可用區(qū)2個隊(duì)列負(fù)載均衡時優(yōu)先從同 AZ 節(jié)點(diǎn)隊(duì)列中進(jìn)行加權(quán)輪訓(xùn)。 當(dāng)同AZ節(jié)點(diǎn)權(quán)重總和/所有節(jié)點(diǎn)權(quán)重總和 < Y%時,同 AZ 節(jié)點(diǎn)優(yōu)先策略失效,退化為所有節(jié)點(diǎn)加權(quán)輪訓(xùn)。 當(dāng)同AZ可用節(jié)點(diǎn) < Z時,同 AZ 節(jié)點(diǎn)優(yōu)先策略失效,退化為所有節(jié)點(diǎn)加權(quán)輪訓(xùn) 。
- 需要新增查詢節(jié)點(diǎn)列表的監(jiān)控埋點(diǎn)&以上三種計算結(jié)果的埋點(diǎn)
圖片
另外 SDK 支持一鍵動態(tài)切換至走老架構(gòu)方式(4層 SLB)
管理后臺
- 新增頁面【節(jié)點(diǎn)管理】,用于查詢&管理節(jié)點(diǎn)
圖片
- 新增頁面【兜底節(jié)點(diǎn)管理】,用于管理兜底節(jié)點(diǎn)列表。
圖片
- 提供節(jié)點(diǎn)上下線 API,給發(fā)布系統(tǒng)調(diào)用。
修改狀態(tài)會去所有 metadata 數(shù)據(jù)庫執(zhí)行,只有一個庫成功就返回成功,如所有庫都修改失敗,則返回失敗。
update node_info
set enabled = 0, config_version = #{config_version}
where ip = ? and port = ?
容災(zāi)能力
表格中的是否有影響和故障恢復(fù)時間均指 SDK-Proxy 的訪問鏈路,Proxy-DB 鏈路不在范圍內(nèi)。
圖片
- 可用區(qū)i全部宕機(jī)舉例
參考以下時間線,可在30s左右完成恢復(fù)。
圖片
- i區(qū) Metadata 數(shù)據(jù)庫故障,無影響。
圖片
一些思考
Q:為什么不用 sylas(得物注冊中心產(chǎn)品)做注冊中心,而是要自建元數(shù)據(jù)中心做服務(wù)發(fā)現(xiàn)?
彩虹橋和 sylas 均為 P0 級別服務(wù),對穩(wěn)定性要求極高,在架構(gòu)設(shè)計之初需要充分考慮到互相依賴可能帶來的級聯(lián)故障,在與注冊中心相關(guān)同學(xué)溝通后,決定自建彩虹橋元數(shù)據(jù)中心,實(shí)現(xiàn)自閉環(huán)。
Q:為什么不是傳統(tǒng)的基于 Raft 協(xié)議的三節(jié)點(diǎn)來實(shí)現(xiàn)服務(wù)發(fā)現(xiàn),而是用多套數(shù)據(jù)源做 merge?
Raft 是工程上使用較為廣泛的強(qiáng)一致性、去中心化、高可用的共識算法,在分布式系統(tǒng)中,適用于高一致性、容錯性要求高的場景。但 Raft 協(xié)議需要維護(hù)領(lǐng)導(dǎo)者選舉和日志復(fù)制等機(jī)制,性能開銷較大,其次 Raft 協(xié)議相對復(fù)雜,在開發(fā)、維護(hù)、排障等方面會非常困難,反之采用多數(shù)據(jù)源求并集的方式更簡單,同時也具備單節(jié)點(diǎn)故障、整個可用區(qū)故障以及跨區(qū)網(wǎng)絡(luò)中斷等多種復(fù)雜故障下的容災(zāi)能力。
Q:如何在 SLB 切換到新架構(gòu)的過程中保障穩(wěn)定性?
可灰度:支持單個上游節(jié)點(diǎn)粒度的灰度
可回滾:支持一鍵動態(tài)切換至 SLB 架構(gòu)
可觀測:大量埋點(diǎn)數(shù)據(jù)可實(shí)時進(jìn)行觀測,有問題可快速回滾。
圖片
圖片
六、總結(jié)
自建元數(shù)據(jù)中心后,將給彩虹橋帶來一系列收益:
- 應(yīng)用服務(wù)通過 SDK 直接連接 Proxy 節(jié)點(diǎn),擺脫了對 SLB 的依賴,解決了帶寬瓶頸和額外網(wǎng)絡(luò)開銷問題,并提高了流量灰度控制的精細(xì)度。
- 簡化了擴(kuò)容流程,擴(kuò)容時只需增加 Proxy 節(jié)點(diǎn)大大縮短整個擴(kuò)容時間。
- 多可用區(qū)容災(zāi)實(shí)現(xiàn)自動故障轉(zhuǎn)移,無需人工干預(yù)。
- SDK 具備了同 AZ 路由能力,更好適配雙活架構(gòu)。