羽量級實現靈活通用的微服務流量分發

背景

伴隨着業務的飛速發展,達達集團(NASDAQ: DADA) 內部的微服務數量和節點個數也都在不斷增長。經歷了六年時間,我們也從公司成立最開始的單一大服務,逐步發展到了幾百個雲服務,幾千臺雲主機的規模。

當業務邏輯和運行環境越來越複雜,簡單的服務發現和治理功能已不能滿足我們的需求。相信和許多公司一樣,一路走來我們碰到了許多問題:

  1. 在線壓測流量隔離:我們希望在與生產保持一致的環境下進行完整的壓力測試,但是卻苦於沒有好的辦法將線上真實的生產流量與壓測流量隔離開來,我們只能選擇耗費大量的人力物力來部署一套與生產一模一樣的環境,極度影響壓力測試的效率。
  2. 同服務並行測試:隨着團隊擴大,同一業務服務中會有多個功能在被同時開發和測試,但是因爲缺乏好的方法將同一服務的不同版本在測試環境中隔離,這些不同功能只能被串行獨立測試,嚴重影響了測試的效率,讓產品的迭代週期變長。
  3. 核心服務無法冷熱分離:我們有部分的核心服務同時承載着在線的實時業務流量與內部一些離線job的查詢流量。其中,前者需要保證性能和功能的穩定,而後者的流量一般會消耗很多的計算資源但是對性能沒有嚴苛的要求。在高峯時期,來自後者的流量會因爲佔用大量資源而對前者的在線流量產生負面影響。
  4. 多服務灰度發佈:一些業務的變動往往需要多個微服務系統代碼同時更新,而在灰度發佈過程中,我們沒辦法控制新版本的調用方只調用新版本的服務,使得很多問題不能在灰度發佈的過程中被發現,全量上線後引發線上故障。
  5. 服務間的限流:在異常場景下,來自一些非核心項目的大量流量可以讓核心服務癱瘓。在沒有靈活的限流手段之前,我們只能通過緊急擴容和代碼修復來解決問題,拉長了事故恢復的時間,給業務帶來損失。

要解決以上的痛點,本質上其實是要求我們在微服務體系內部進行(基於服務組,接口等多個維度的)靈活流量分發。

需求與架構設計

我們調研了市面上已有的一些現成解決方案,如阿里巴巴的Dubbo,但是因爲各種原因(如接入成本高昂,以及達達內部微服務的實現語言多樣)沒辦法被我們直接使用。所以我們最終決定自己實現一套通用靈活的服務治理和流量分發引擎。

由於我們的業務服務情況複雜,業務功能的發展和變化非常快,且可分配在這個項目上的工程師資源並不充裕,所以我們從設計初期,就訂立了幾個大的原則:

  1. 我們希望這套引擎除了可以解決我們自身已經遇到的問題之外,還可以儘量多的覆蓋未來可能遇到的各類同質化問題。
  2. 這套引擎的實現應該與與編程語言無關,否則無法適應達達內部複雜多樣的應用服務技術棧。
  3. 實現需要輕量,不要引入過多的外部依賴,捨棄不必要的功能。否則工期太長業務等不了,且越複雜的系統,穩定性的風險就越大。

在此之前,我們有一套基於consul的服務發現框架,它已經包含了服務發現最核心的功能,只是流量分發的最小粒度只能到服務級別。但是由於consul天生擁有kv存儲和元數據(metadata)更新的能力,使其具備了一定的擴展能力。而一些需要動態變化的路由規則等配置,我們決定利用配置中心的能力來進行存儲,以便我們可以實時更新和分發。

基於這些現狀和原則,我們對整體系統的架構做出瞭如下圖所示的設計:

  1. 我們最終決定將新功能的所有邏輯做到客戶端中,而consul服務只用來關心節點的健康狀態以及存儲元數據(metadata)。這樣做的好處是核心的服務發現可以與業務邏輯最大程度的解耦,且對服務端最小限度的修改也使得整體的成本和風險相對可控。

  2. 除metadata外,流量分發策略都以配置形式存儲在我們的配置中心中,且提供了一系列管理配置工具供用戶去查詢和修改。這樣可以讓我們實時靈活的在運行時來管控整個集羣中的流量。

詳細設計

元數據模型

我們對想要解決的問題做了進一步的抽象後,發現我們只需要對現有的服務註冊模型做以下兩個維度的擴充,就足以讓我們在客戶端中實現靈活的控制邏輯來管控流量:

  1. “鏈路”:在壓力測試和灰度測試的場景下,我們需要將多個服務的不同節點組合在一起形成一個虛擬的隔離環境。我們將這個包含多個服務不同節點的容器稱爲“鏈路”,如下圖所示:

在上面的示例圖中,請求可以被分發到兩個不同的鏈路(“鏈路A”與“鏈路B”)中。在默認情況下,流量只會在同一鏈路下流轉(身處"鏈路A"中的服務只會發送請求給"鏈路A"中的下游服務)。在達達的真實環境中,除微服務外,我們的數據源以及核心中間件如消息隊列等也被納入了鏈路管理之中,所以任何請求都可以在一個完整的鏈路中被處理。

但是在真實場景中,不是所有的鏈路都會包含完整的微服務拓撲(如在測試環境中,測試人員只需要對需要測試的服務創建一個鏈路即可)。此時就需要我們根據鏈路的不同特點和需求,來決定流量(在本鏈路中找不到下游服務節點的情況下)是否需要被轉向至其他鏈路中。於是我們將“鏈路”又細分爲兩種類型:“強鏈路”(不允許流量轉向到其他鏈路,比如生產鏈路和壓測鏈路,他們二者之間流量完全不允許互串)與“弱鏈路”(可以將流量轉向至其他鏈路,比如測試環境中同一服務的不同分支)。

  1. “服務實例分組”:爲了解決服務冷熱分離等問題,需要我們對同一服務下的實例節點進一步細分爲不同的分組。一個典型的場景如下圖所示:

在上面的示例圖中可以看出,通過對服務B的實例節點進行分組後,我們就可以對流量進行靈活的調度和分發了。

以上的元數據信息因爲不會經常變動,且都是與實例節點相關的屬性,所以我們決定將這些元數據信息作爲實例節點的屬性,與實例信息一起存儲在consul中。

路由規則和邏輯

在對實例節點的元數據進行了擴充和完善之後,我們只需要在這些元數據的基礎上構建我們自己的路由規則邏輯就可以了。

爲了便於擴展以及可以動態分發,我們將路由規則以json形式存儲於配置中心中。並且爲了更便於研發工程師操作和配置,我們還提供了一套在線工具。具體的路由規則舉例如下:

在有了定義好的路由規則之後,我們就可以很輕鬆的在客戶端來撰寫路由的業務邏輯了,具體路由邏輯如下圖所示:

具體來說,在一個請求即將從A服務發送到B服務之前,需要做如下的邏輯判斷才能決定該請求的真正去向:

  1. 根據發送方本機所處的“鏈路”:默認只會選擇和自己處於相同鏈路的B服務節點。如果未找到可用節點,則會根據自身所處鏈路的強/弱(前一章節有介紹)屬性來判斷。
  2. 根據發送方自身的服務名,本機的IP,請求的接口等信息匹配路由規則:如果匹配到規則,則根據該路由規則指定的目標來進行路由。

真實場景應用

結合我們的解決方案,讓我們再次回顧一下第一章中描述的各個問題。

通過“鏈路”這個簡單的概念模型,我們可以:

  • 在生產環境中分配隔離一批節點加入到“壓測鏈路”,其作爲一個與“線上流量鏈路”共處於生產環境中的強鏈路,兩者的流量被完美的隔離。
  • 在測試環境中,同一服務的不同開發分支可以被隔離到不同的鏈路中,使得針對同一服務的並行測試成爲可能。
  • 灰度發佈過程中,只需要將一同參與發佈的服務都加入到灰度鏈路中,那麼就可以確保進入灰度鏈路的流量只會在灰度的服務中流轉。

通過服務實例分組和靈活的路由功能,我們可以通過動態配置規則就可以實時調控集羣中的流量,來實現服務冷熱分離和限流等。

這套解決方案也給我們的業務提供了巨大的價值。舉例來說,通過使用“鏈路”系統在線上壓測中的應用,不但讓我們可以完美的在線上真實環境中進行壓力測試,同時還極大的縮短了壓測環境準備,應用調試等環節所需要的時間,使得我們整個壓測流程的資源投入從2018年雙11的250人日縮短到了2019年雙11的70人日,效率提升了350%。

總結

針對我們在業務和系統發展中遇到的各類流量調控需求和問題,我們提出了一套簡單易懂的數據模型,通過低成本輕量的邏輯實現,使得這些需求和問題得以被非常好地解決。同時這套解決方案因爲足夠簡單通用,讓我們在幾乎不需要任何擴展的情況下就可以支持未來可能出現的各類流量相關的需求。

這套系統自上線以來,幫公司節約了大量的技術成本,提升了人效,還規避了很多事故可能造成的潛在損失。

團隊成員

  • bowl-gu: 達達集團架構師。微服務治理,數據源高可用等模塊的負責人
  • doubleMing: 達達集團架構師。主要負責服務治理,數據源高可用等系統的設計開發與維護
  • superbool: 達達集團架構師。主要負責監控報警平臺,流量動態路由等系統
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章