註冊中心作為微服務架構的核心,承擔服務呼叫過程中的服務註冊與定址的職責。註冊中心的演進是隨著業務架構和需求的發展而進行演進的。騰訊當前內部服務數超百萬級,日呼叫量超過萬億次,使用著統一的註冊中心——北極星。騰訊註冊中心也經歷 3 個階段演進歷程,下文主要分享騰訊內部註冊中心的演進歷程,以及根據運營過程中的最佳化實踐。
服務註冊中心概述
2008 年,zookeeper 誕生,作為最早被廣泛使用的註冊中心,提供了可自定義的基於樹形架構的資料儲存模型。業界常見的微服務使用場景是 dubbo 框架,使用 zookeeper 進行服務資料的管理;同時在大資料場景下,kafka/hadoop 使用 zookeeper 進行叢集和分割槽的管理,屬於泛服務註冊發現的用法。由於 zookeeper 沒有服務模型的概念,各框架使用約定的樹形路徑進行服務資料的存取,在使用和維護上比較複雜。2014 年開始,隨著微服務架構的大規模應用,具備統一的服務模型和控制檯的註冊中心得到廣泛的使用,最常用的包括 eureka、consul、nacos 等。2015 年起,k8s 開始大規模使用,k8s 基於 etcd+coredns 提供了基於域名的服務發現能力,應用直接基於 DNS 即可進行服務發現。
總體來說,服務註冊發現有兩種實現方案。
zookeeper/eureka/nacos/consul:優勢在於作為獨立註冊中心,與具體應用部署環境無關,管理和使用都比較方便。侷限在於這些註冊中心架構都是儲存計算合一的,單叢集效能有限,無法單獨針對讀或者寫進行水平擴充套件。
k8s service:服務資料基於 etcd 進行儲存,透過 coredns 進行服務發現。優勢在於服務註冊中心內建在 k8s 平臺中,無需額外維護註冊中心。侷限點在於:
(1) 無法實現針對 pod/workload 粒度基於權重的流量灰度。
(2) 應用服務呼叫時,只能獲取到單個 cluster ip,無法拿到基於的 pod ip 列表進行負載均衡,長連結使用場景下容易出現負載不均的問題。
(3) 應用基於 TCP 連線無法拿到對端的 POD IP,出問題定位不方便。
(4)k8s 服務預設只支援當前叢集發現,跨叢集呼叫的域名需要帶入叢集資訊,應用進行服務呼叫時需區分並使用不同的域名。
(5) 只能基於 k8s 環境使用,非 k8s 環境部署的服務無法使用。
騰訊自研註冊中心演進歷程
第一階段:分散式服務萌芽期
在 2012 年以前,騰訊內部主流開發語言是 C++,業務技術棧主要是 LAMP(Linux+Apache+MySQL+PHP)模式和 CGI+ 獨立後端模式,前者常見於邏輯簡單的單體 Web 應用,後者常見於大型偏計算類的應用,比如社交應用的後端等。
在這 2 種模式下,業務的遠端呼叫(PHP 定址 MySQL,CGI 定址後端節點),需要依賴定址。在沒有註冊中心之前,業務往往透過 VIP(四層負載均衡)的方式進行定址,存在以下問題:
變更難度大:VIP 本身基於四層負載均衡裝置實現,裝置本身存在一個裁撤變更的風險,一旦出現 VIP 變更,業務的程式碼需要進行變更,影響面比較廣。
單點問題:VIP 本身成為了一個單點,一旦四層負載均衡裝置出現問題,會導致服務不可用。
在 2007 年,為了解決 VIP 定址所帶來的問題,騰訊自研了一款註冊中心產品 L5,提供了基於控制檯和 SDK 的服務註冊發現、負載均衡功能,服務呼叫可以透過 L5 定址後直連訪問,無需再依賴 VIP。
L5 是一套使用 C++ 開發的一體化註冊中心,基於資料庫進行服務資料的儲存,客戶端透過 Agent 與服務端進行訪問。L5 的架構存在以下問題:
架構封閉:註冊所有的功能和負載均策略都寫死在程式碼中,不具備任何擴充套件性,業務的新功能需求只能透過 L5 的維護團隊來支撐。
第二階段:多註冊中心
在 2013 年後,隨著移動網際網路的高速發展,社交、娛樂等業務的使用者量也在增長,分散式服務架構進入快速發展期,對註冊中心也提出了一些需求,比如支援健康檢查、支援輕量化、支援與基礎設施或釋出平臺打通等。但是由於原有 L5 缺乏定製性,而且 L5 的運維團隊縮減,導致業務提的需求無法滿足,因此部分業務都開始自研註冊中心,其中使用比較廣泛的是以下幾個註冊中心:
CMLB:全稱 common load balance system,提供同 IDC、同城、同國、同洲等多級容災部署及就近定址功能,滿足業務海外國內多地部署就近容災的訴求。
TSeer:一個輕量級的註冊中心,支援 Agent 與 SDK2 種接入方式,滿足輕量化的訴求。
Routersrv:是微信服務框架 SvrKit 的一部分,打通並關聯平臺相關的基礎設施及釋出系統。多註冊中心共存,不同註冊中心上的資料是隔離的,無法互通。
使用不同註冊中心的業務團隊要進行互通,服務提供者需要把服務同時註冊到多個註冊中心,才能支援各個業務團隊之間服務相互發現。
這種使用方式會帶來以下問題:
1.服務釋出的複雜度提升:服務的部署和釋出一般和釋出系統繫結,對接多個註冊中心,對釋出系統的實現會比較複雜,容易出現資料不一致的問題。
2.業務開發複雜度提升:不同註冊中心有不同的服務發現 API,業務開發者在呼叫服務之前,需要先確認服務註冊在哪個註冊中心,選用不同的 API 介面來進行服務發現。
第三階段:大規模分散式服務互聯互通
到了 2018 年,公司內部業務經過進一步的發展,節點數已經達到了百萬級,日呼叫量也超過了萬億,內部服務跨業務團隊之間互相訪問成為了常態。使用多註冊中心所引發的問題,愈發成為了影響業務開發和部署效率的瓶頸。
統一註冊中心:北極星
2018 年到 2019 年初,在公司開源協同的背景下,為解決多註冊中心帶來的複雜性問題,多個業務團隊的同學們聚在一起,基於原有註冊中心的運營和設計經驗,透過內部開源的方式,協同共建了可以支撐百萬級服務體量的註冊中心——北極星。
註冊中心如何無縫遷移
新的註冊中心上線,但是大量的存量服務節點在老註冊中心上,業務團隊需要進行漸進式遷移,但是對於業務團隊來說,不希望有任何的程式碼改造,否則這個遷移過程成本太高。因此北極星支援以下零程式碼改造的的漸進式遷移方式:
1.雙註冊雙發現
對於 Java 類應用,北極星透過提供 JavaAgent 的方式,支援已遷移的服務透過零改造的方式,進行雙註冊和雙發現,同時存量註冊中心也有全量的服務,未遷移服務也可發現已遷移服務。
2.單向同步 + 協議相容
對於非 Java 類應用,北極星透過外掛化提供協議相容能力,相容已有註冊中心的介面,新服務變更一下注冊中心地址即可實現遷移。同時,為了解決未遷移服務訪問已遷移服務的問題,透過擴充套件存量註冊中心的方式,實現存量服務資料的單向同步以及對已遷移服務的增量拉取。
北極星 VS 其他註冊中心
北極星在 2018 年底開始進行設計和開發,在 2019 年初上線,到現在已運營了超過 3 年時間,支撐了騰訊內部不同形態的業務接入,也經歷過大大小小的運營活動的洗禮,架構和穩定性也得到了打磨。從技術設計上,北極星解決了業界註冊中心存在的單叢集效能,水平擴充套件的問題,同時產品形態上也有自己的一些思考,下文會對這部分內容進行分享:
優勢 1:可擴充套件性
註冊中心關鍵的點是服務資料的一致性,根據一致性模型,註冊中心會分為 2 類:
強一致性(CP)模式
註冊中心叢集中所有節點的資料都保證強一致,客戶端從叢集中同一時刻任意一個節點獲取到的資料都是相同的。強一致性叢集中,各個節點有自己的角色,一般分為 leader 和 follower。leader 統一協調資料的寫入與同步,follower 負責處理客戶端的讀請求,常見的資料一致性協議有 Zab,Raft 等。
zookeeper 和 consul 都屬於強一致性註冊中心,其侷限點在於單叢集的寫效能受制於一致性協議,必須等待 leader 寫入成功且叢集中大多數的 follower 都同步成功,才完成一次寫操作。當 leader 節點網路故障,需要重新選主,通常耗時 30~120 秒,選舉期間叢集不提供服務,這不符合可用性原則。
最終一致性(AP)模式
註冊中心叢集所有節點都是無狀態對等的,服務資料可以在任意節點寫入,寫入後透過遠端請求同步到叢集其他節點。客戶端從叢集中同一時刻不同節點獲取到的資料可能會存在差異,但是最終會保持一致。
eureka 和 nacos 都屬於最終一致性註冊中心,其侷限點在於叢集每個節點都參與服務資料寫入、同步、以及讀取,計算儲存合一。單叢集的讀效能會受到寫操作的影響,寫操作過於頻繁引起高負載問題,對讀操作效能存在影響。
考慮到作為註冊中心,可用性、效能和吞吐量是比較關鍵的指標,短期的資料不一致是可以忍受的,因此在架構模型上,北極星採用的是最終一致性的架構模型。
計算儲存分離
騰訊內部服務呼叫鏈路是一個網狀結構,各個 BG、各個業務之間都存在相互呼叫,比如遊戲業務需要對接支付系統,影片業務需要對接儲存系統等。為了簡化使用者的使用方式,北極星透過統一叢集來支撐內部所有的服務接入,以及隨著業務的發展,叢集的效能也要可以支援水平擴充套件。
因此,北極星採用計算儲存分離的架構,控制面程式碼拆分成 cache(快取)層和 store(儲存)層,cache 層提供高效能快取的能力,store 層負責對接後端儲存,支援關聯式資料庫和本地磁碟;客戶端及控制檯把服務資料註冊到 store 層,store 層透過非同步的方式將資料寫入資料庫或者磁碟;cache 層訂閱到變更後,從 store 層增量拉取服務資料,並更新本地快取;客戶端直接從 cache 層獲取到所需的服務資料。控制面 cache 本身無狀態,這意味著對於讀寫來說都能很好的水平擴充套件,在騰訊內部,從小規模叢集(萬級以下服務)到大規模公共叢集(百萬級服務)都有著生產案例。
優勢 2:單叢集效能
為了提升控制面效能,觀察到大部分註冊中心在服務發現過程中,都有著模型轉換和編解碼的過程,這一過程 CPU 基本都是消耗在了 protobuf 的編解碼動作中(佔 70%)。因此,基於空間換時間的思想,我們在公共服務快取基礎上,增加了協議層熱點快取,對返回的編碼後的資料進行復用,省去編解碼過程的損耗;經現網實際資料驗證,協議層快取命中率高達 98%,效能相比之前提升了一倍,同時也優於同類註冊中心。
效能對比
我們針對北極星控制面進行了壓測,對於不同規格下的北極星三節點叢集、eureka 叢集、consul 叢集,壓測資料如下:
1.服務註冊
2.服務發現
3.測試結論
透過對北極星的註冊以及發現功能,從介面層到儲存層全呼叫鏈路的最佳化,從最終的壓測結果可以看出:
(1)在服務註冊的 TPS 上,北極星註冊效能,對於同等規格的 eureka,最高有將近三倍的註冊效能提升;對於同等規格的 consul,最高有將近四倍的註冊效能提升。
(2)從服務發現的 TPS 上,北極星的發現效能,對於同等規格的 eureka,最高有將近十七倍的服務發現效能提升;對於同等規格的 consul,最高有將近兩倍的服務發現效能提升。
優勢 3:針對不同企業規模有不同的部署模式
針對企業不同的微服務規模,北極星提供 3 種形態的叢集組網:
1.大規模叢集組網,按功能拆分叢集部署,可支援百萬級的服務接入;
2.小規模叢集組網,全功能對等叢集,可支援十萬級以下服務接入;
3.單機版本,提供輕量化的單機版本,供開發人員本地測試聯調使用。
大規模叢集(百萬級服務)
北極星控制面是支援模組化組裝的,各部分功能和介面,比如註冊、發現、配置等,可以透過單獨開關控制開啟和關閉。為了提升功能的可用性,實現故障隔離,騰訊內部實踐中,對北極星按照功能模組進行叢集拆分,服務註冊、服務發現、控制檯操作劃分為不同叢集來進行處理,各叢集相互獨立,可按照請求量獨立擴充套件。客戶端透過埋點叢集的二次定址機制,為介面定址到目標叢集進行功能介面的呼叫。
小規模叢集(萬級以下服務)
大規模叢集部署流程相對比較複雜,且耗費資源較多,為了簡化部署過程,北極星支援全功能叢集,可支撐萬級以下級別的服務接入。每個北極星節點都是具備了全部的功能,如果當前北極星叢集的負載高的話,只需對北極星叢集執行水平擴容操作。
單機版(本地開發)
開發人員在程式開發聯調過程中,往往需要依賴註冊中心,而依賴公共叢集註冊中心容易產生環境衝突,導致聯調結果不準確。北極星也提供單機版能力,開發人員可以在桌面機啟動一個個人獨佔全功能的北極星註冊中心進行調測,調測完可隨時銷燬。
優勢 4:註冊中心、服務網格、配置中心一體化
早期的註冊中心,如 zookeeper, eureka,只提供了服務註冊和發現功能,對於簡單的服務呼叫來說是比較適合的。但是在應用的整個生命週期中,服務呼叫往往涉及複雜的排程場景,比如在騰訊內部,應用測試階段需要涉及多測試環境的隔離、在釋出階段需要進行灰度釋出和金絲雀釋出,在生產環境需要進行基於生產流量的 A/B 測試。這時候就需要基於請求特性進行流量排程的能力,將服務流量進行精細化匯入到對應的服務分組中,也就是現在常說的服務網格。要實現服務網格,除需要依賴註冊中心下發服務資料外,還需要進行網格規則(流量的排程策略等)的管理下發。此外,應用自身的執行所需要的業務配置資訊,也需要依賴配置中心進行管理及訂閱下發。
為了簡化使用者對服務網格的使用,業界像 consul 2.0 提供了註冊中心、配置中心和服務網格的解決方案。與 consul 類似,北極星控制面將註冊中心、服務網格、配置中心功能進行了整合,提供了一體式的服務網格控制面,同時也提供異構資料面及業界主流框架(Spring Cloud,gRPC,TARS 等)擴充套件,便於應用整合。
總結
騰訊內部服務架構的發展經歷了從單體到分散式再到微服務的發展歷程,而服務架構的核心元件註冊中心,也經歷了從 L5 單體註冊中心,到多註冊中心共存,最終統一到北極星的發展歷程。北極星作為騰訊現階段的企業級註冊中心,支撐了騰訊萬級服務,以及百萬服務例項的日常業務請求呼叫。根據業務不同的需要,支援大規模叢集、小規模叢集、單機版等多種部署形態,在運營過程中經過多次的迭代最佳化,註冊和發現效能均優於同等規格的開源註冊中心。北極星已對外開源(開源後名字為 Polaris Mesh),支援 Spring Cloud、Spring Boot、gRPC、dubbo 等主流框架應用直接接入,歡迎感興趣的開發者、企業進行體驗及參與一起共建。歡迎大家加入北極星交流群:
相關連結
北極星官網:北極星(https://polarismesh.cn/)
北極星程式碼庫:Polaris Mesh · GitHub(https://github.com/PolarisMesh)