
作者 | 周曉軍 愛奇藝中間件團隊負責人
導讀:本文整理自作者于 2020 年雲原生微服務大會上的分享《愛奇藝在 Dubbo 生态下的微服務架構實踐》,重點介紹了愛奇藝在 Dubbo、Sentinel 等開發架構方面的使用經驗以及微服務生态體系的建設經驗。
阿裡巴巴雲原生公衆号背景回複 818 即可擷取直播回看位址和大會 PPT 合集。
本文将主要圍繞以下幾個主題展開:
- Apache Dubbo 簡介及其在愛奇藝的發展曆史
- 愛奇藝内部對 Dubbo SDK 的擴充及圍繞 Dubbo 相關的微服務生态建設
- 後續規劃
1. Apache Dubbo 簡介
Apache Dubbo 是一款由阿裡開源的高性能 RPC 架構。Dubbo 架構本身除了通信外,還内置了微服務治理的多項功能(如注冊發現,路由規則等)。
自從 2017 年重新開機維護以來,Dubbo 社群一直保持了較高的活躍度。從周邊生态來看也相對比較完善,比如 Nacos、Sentinel 等開源架構都對其提供了支援。在語言支援方面,除了 Java 語言之外,Dubbo-go 社群目前也非常活躍,且針對 python,nodejs 等主流開發語言 Dubbo 也有一些開源實作。基于以上這些因素,我們決定引入 Dubbo 架構,用以替換原先自研的 RPC 架構。
愛奇藝是在 2019 年 6 月正式開始引入 Dubbo 架構的。我們将其與對接公司内部的基礎設施做了對接,如注冊中心、監控系統等等,并在 2019 年 8 月正式釋出了第一個内部版本。
這裡值得一提的是我們并沒有維護自己的 Dubbo 分支,而是利用 Dubbo 強大的擴充機制開發我們的新特性,這樣使得我們能夠在跟進 Dubbo 社群新版本方面沒有障礙。在這個内部版本釋出後,很快就有第一個生産應用在同年 9 月份上線。後續我們在進一步擴充 Dubbo SDK 功能的同時,在周邊生态建設方面也做了不少工作,其中就包括在 2020 年 3 月份上線的 Nacos 注冊中心等。
2. Apache Dubbo 在愛奇藝的發展曆史
優秀的微服務開發架構是業務服務化的基石,但是由于微服務應用的複雜性,要幫助業務團隊更好地實踐微服務架構,還需要一個相對完善的微服務生态體系作為支撐。
微服務生态體系
下圖展示了目前愛奇藝内部微服務生态體系的全景。
這裡面分為幾個層面:
- 首先是開發架構層面,Dubbo SDK 內建了注冊發現、通信及負載均衡的能力,但是類似熔斷及限流功能還是需要采用 Sentinel 等架構來進行支援;
- 在基礎設施層面,注冊中心/配置中心均是微服務生态中重要元件;此外為了保證應用的可用性,完整的監控體系也必不可少,如名額監控、日志監控、鍊路追蹤等;
- 最後,為了友善運維人員管理微服務應用,還需要一套功能完善的管理平台,其中包括了服務管理、配置下發、監控告警及一些對開發人員的支援功能。
可以看到,整個微服務的生态體系還是非常龐大的,限于篇幅,以下的演講會主要會集中在以下幾個方面展開:
- Dubbo SDK 的擴充
- 生态體系建設
- 注冊中心的演進
- 監控體系的建設
- 熔斷限流方面的支援
1. Dubbo SDK 的擴充
根據愛奇藝内部的實際情況,以及各個業務團隊的需求,我們主要對 Dubbo SDK 做了以下幾方面的擴充:
- 基礎設施的适配:包括注冊中心、監控系統、内部的容器平台等等;
- 可用性增強:包括非健康執行個體隔離以及區域就近路由的機制;
- 安全性增強:支援了服務間調用的認證機制;
- 序列化:增加了對 protobuf 序列化方式的支援。
1)非健康執行個體隔離機制
首先介紹非健康執行個體隔離機制。
Dubbo SDK 預設采用随機的負載均衡政策,并通過失敗重試的政策來保證調用的成功率。通過實踐發現,生産環境中有時會出現少數 Provider 節點雖然已經處于不健康的狀态(比如磁盤寫滿等),但是還是能與注冊中心進行正常通信,這樣 Consumer 端還是能發現這些執行個體,導緻部分請求還是會被分發過去。這些請求由于執行個體本身的問題,可能會出現響應時間變慢或者錯誤率上升,進而引起整個服務品質的下降(響應時間抖動或整體調用成功率下降)。
為了解決這樣一個問題,我們的思路是引入用戶端健康檢查機制,即 Consumer 端會對每個 Provider 執行個體的請求成功率進行統計,判斷其是否健康;對于不健康的 Provider 執行個體,Consumer 端會對其進行隔離一段時間,後續的請求不再通過負載均衡政策發送到這些執行個體上。另外 Consumer 端會維護每個 Provider 執行個體被隔離的次數,如果某個執行個體被多次隔離,每次隔離的時間也會相應變長。
我們的擴充機制中提供了預設的健康檢查政策,包括檢查最近一次調用是否出現服務端異常,或者一段時間内是否有大量發出的請求未被傳回。使用者也可以通過擴充我們提供的接口來實作自己的檢查政策。
為了避免因為網絡抖動等造成的意外影響,我們還設計了一套兜底機制。即當 Provider 執行個體中不健康的比例超過一定門檻值時,Consumer 會忽略執行個體隔離的政策,避免集中的流量将剩餘的執行個體打垮。
2)區域就近路由機制
接下來介紹一下區域就近路由機制。
愛奇藝在多地都建有機房,為了確定在單個機房出現故障時各業務系統仍能正常工作,核心業務一般會采用兩地三中心的架構進行部署。在這種場景下,系統如果産生跨地域的通路請求,由于網絡延時的原因勢必導緻請求延時增大,是以各業務一般都有用戶端就近通路服務端執行個體的需求。
我們通過擴充 Dubbo 的路由機制實作了這樣的政策。大緻的實作原理是,Provider 和 Consumer 執行個體在啟動時,會先從一個公共的地域服務中擷取執行個體目前所在地域資訊(比如可用區等)。Provider 執行個體在服務注冊時會将上述的地域資訊作為 URL 的一部分注冊到注冊中心,這樣 Consumer 執行個體就能夠在服務發現時獲知每個 Provider 執行個體的地域資訊并和自身的地域資訊進行比對,優先選擇臨近的執行個體就近通路。
此外,Consumer 執行個體也會通過上文中提到的健康檢查機制對服務端執行個體進行檢查,如果發現本地域健康的 provider 執行個體低于設定比例時,則會忽略就近路由的政策,改為在所有的 Provider 執行個體中進行負載均衡,進而實作自動的 failover 機制。
3)認證機制
部分内部服務有安全認證相關的需求,不希望非授權應用對其進行通路。為了解決這個問題,我們開發了一套基于數字簽名及 AK/SK 的認證體系。
其基本原理是:
- Provider 服務可以通過配置在某個service上開啟鑒權;
- 需要通路敏感服務的 Consumer 應用,需要在微服務平台上進行申請,審批後會在這個授權關系上生成一對 AK/SK 并同步至鑒權服務;
- Provider/Consumer 與鑒權服務進行通信,擷取相關的 AK/SK,這個過程使用 HTTPS 進行通信;
- Consumer 在發起調用時,會對請求參數等生成一個數字簽名,連同時間戳、AK 等資訊一起發送給 Provider 端;
- Provider 在收到請求時,會對其數字簽名等資訊進行核對,确認請求資訊的來源及資料的完整性。
以上介紹的是我們針對 Dubbo SDK 的擴充内容,接下來主要介紹我們在微服務生态方面的建設。
2. 生态體系建設
注冊中心在微服務應用中是最重要的基礎設施之一,在 Dubbo SDK 引入之初,為了快速落地,我們使用了 ZooKeeper 作為注冊中心。當然實際上 ZooKeeper 并不是微服務注冊中心的最佳選型,它的主要缺點包括:
- 無法橫向擴充;
- 作為一個一緻性的系統,在網絡分區會産生不可用。
1)注冊中心演進
在調研了業界的各個方案後,我們選用了 Nacos 作為我們下一代的微服務注冊中心。下圖右下角是 Nacos 的整體介紹圖,選用 Nacos 的主要原因是:
- 高性能,可以橫向擴充;
- 既适用于傳統為服務架構,也能适用于雲原生環境,包括支援與 Istio 控制面對接;
- 提供了 Nacos-Sync 元件,可以用較低的成本進行注冊中心的遷移。
在部署 Nacos 服務時,我們充分考慮了服務部署架構方面的高可用性。目前我們的 Nacos 服務是一個大叢集,執行個體分布在多個不同的可用區中,在每個可用區内部,我們會申請不同的 VIP,最終的内網域名是綁定在這些 VIP 上。另外其底層所使用的 MySQL 也采用了多機房部署。這樣的架構可以避免單個 Nacos 執行個體或者單機房故障造成整個 Nacos 服務的不可用。
以下是一些可能的故障場景的模拟:
- 單個 Nacos 執行個體故障:利用 Load Balancer 叢集提供的健康檢查能力自動從 VIP 中摘除;
- 某個 VIP 叢集故障:利用用戶端重試機制解決;
- 單個 AZ 故障:利用用戶端重試機制解決;
- MySQL 叢集故障:MySQL 與注冊發現過程無關,不受影響;
- 整個 Nacos 服務故障:用戶端兜底機制,如服務執行個體緩存等。
接下來将簡單介紹一下如何使用 Nacos-Sync 進行注冊中心的平滑遷移。
- 首先要部署一個 Nacos-Sync 服務,從舊的注冊中心向 Nacos 同步資料。Nacos-Sync 支援叢集化部署,部署多個執行個體時,其向新注冊中心的寫入時幂等的,并且它原生支援 Dubbo 的注冊資料格式;
- 檢查資料無誤後,首先更新 Consumer 端,改為從 Nacos 注冊中心進行發現。這時的服務發現的資料均是由 Nacos-Sync 從舊的注冊中心同步過來的;
- 再更新 Provider 端,改為向 Nacos 進行服務注冊;
- 下線 Nacos-Sync 服務及舊的注冊中心,整個遷移流程就結束了。
2)監控體系建設
接下來主要介紹我們内部微服務監控體系的建設。完整的微服務監控體系一般由以下 3 個方面組成:
- 名額監控:包括 QPS / 響應延時 / 錯誤率等黃金名額、業務的自定義名額、JAVA 應用的 JVM 名額,此外還需要采集和基礎環境的相關名額,包括 CPU / 記憶體使用率等;
- 日志監控:如錯誤日志的數量;也可以利用 AI 技術,對日志的模式進行統計分析等;
- 鍊路監控:由于微服務調用關系的複雜性,調用鍊追蹤也是非常必要的,它可以幫助業務人員更好地分析應用間的依賴關系,并能夠監控各個調用關系上的核心名額。
名額監控方面,我們内部圍繞着 Prometheus 建設了一套較為完整的監控和告警的方案。這裡面要解決幾個問題:
首先是名額計算的問題,為了降低侵入性,我們在 skywalking agent 的基礎上進行了二次開發,可以自動攔截 Dubbo 的調用,統計其調用次數、處理耗時、是否錯誤等等。
其次是名額采集的問題,Prometheus 是采用拉模式采集名額的,對于微服務場景一般是利用 Prometheus 的服務發現機制。Prometheus 預設內建了 consul、K8s 等服務發現方式,不過并未對 Nacos 注冊中心直接提供支援,我們在開源的 Nacos adapter 的基礎上進行了改造,使得 Prometheus 能夠從 Nacos 中發現要采集的應用執行個體資訊。
名額檢視主要采用了 grafana,我們提供了一套通用化的配置模闆,業務也可以根據需要自行擴充。
告警方面,我們将告警政策設定在 Prometheus 中,具體的告警會由 alert-manager 通過 adapter 發送給内部的監控告警平台。
監控 dashboard 檢視、告警政策設定、訂閱的入口統一設定在我們内部的全鍊路監控平台上,使用者可以在該平台上檢視進行相應的操作。
下圖展示的是服務監控界面:
鍊路追蹤的基本原理也和 google 關于 Dapper 的論文一緻,應用程式通過埋點的 agent 産生調用鍊資料,通過日志采集或者網絡直接上報的方式統一彙總至 kafka,通過我們的實時分析程式進行分析。
分析結果大緻可以分為三類:
- 原始的調用鍊資料我們會使用 ES+HBase 進行存儲;
- 調用關系上的實時監控資料我們采用時序資料庫 druid 進行存儲;
- 拓撲關系采用圖資料存儲。
3)熔斷限流
最後簡單介紹一下利用 sentinel 架構進行熔斷和限流的相關内容。
由于微服務架構的特點,上下遊依賴和網絡通信都比較多,這些因素都會對應用本身産生一定的風險,比如上遊系統的突發流量或者熱點參數;下遊系統服務不可用、延時增大、錯誤率升高等等。如果缺少對自身系統的保護,有可能産生雪崩的效應。為了應對這些場景,我們主要引入了 Sentinel 架構進行解決。
Sentinel 的核心原理是使用者可以定義各類資源(資源可以是本地的一個接口,或者遠端的某個依賴),并在資源上設定各種規則(比如限流規則),在通路某個資源時,Sentinel 元件會檢查這些規則是否滿足,在不滿足的情況下會抛出特定的異常。使用者可以通過捕捉這些異常實作快速失敗或者降級等業務邏輯。Sentinel 還提供了一個控制台,可以用來管理規則的參數設定以及檢視實時監控等。
為了适應内部業務團隊的需求,我們對 sentinel 架構也做了一些擴充,下面的例子即是我們實作的複雜參數限流功能。Sentinel 架構本身就自帶熱點參數限流的功能,不過僅支援一些簡單類型的參數(如 String、int 等)。在有些情況下,限流的場景可能比較複雜,比如下圖中,可能要根據第一個參數的 id 屬性進行限流,這種場景原生的 sentinel 并未提供支援。針對這種情況,我們提供了一個抽象的接口,允許使用者通過自己的實作從參數中提取出需要限流的資源。
為了實作規則參數的動态下發,我們将 sentinel 與内部的配置中心進行了适配。在 sentinel dashboard 上進行的參數改動,最後都會儲存至配置中心,業務系統通過引入配置中心的 SDK,即可實作在不重新開機應用的前提下進行參數的動态調整。
在我們的微服務管理平台上,還提供了 sentinel dashboard 的托管功能。
發展現狀及開源貢獻
愛奇藝引入 Dubbo 的時間并不長,但是由于其較為穩定的線上表現使得的各個業務團隊的整體接受度較高,上線的規模也在快速增長。短短一年内累計已上線了一百多個線上服務,執行個體數也已經超過五千個。
此外,我們也在使用過程中積極回饋社群,截止目前共送出了三十個左右的更新檔,上文中提到的認證機制也已貢獻給社群,成為 Dubbo 2.7.6 版本的新特性之一。在這個過程中,團隊中也誕生了一位社群的 committer。
未來規劃
對于未來的規劃,大概有以下幾方面的工作:
- 雲原生與 service mesh 已經是微服務技術演進的一個趨勢了,且在一些公司已經有了比較大規模的實踐。如何将 dubbo 與 service mesh 結合,如何提供平滑過渡的解決方案,将會是我們今後工作的一個重點;
- 在服務治理方面,後續我們希望能夠建立一個對 service mesh 和傳統微服務都适用的控制面;
- 在開發者支援方面,我們計劃推出項目腳手架以及線上調試等服務,使得開發人員能更友善地進行項目開發,以及線上問題的排查等。
“ 阿裡巴巴雲原生 關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的公衆号。”