如何解決 Spring Cloud 下測試環境路由問題

語言: CN / TW / HK

作者

張樂

開源社區 Spring Cloud Tencent PMC,開源社區 Apollo PMC,騰訊雲技術專家、 專注於微服務中間件研發包括:註冊中心、配置中心、服務治理中心等。

張皓天

開源社區 Spring Cloud Tencent PMC,騰訊雲高級研發工程師,專注於微服務架構領域,深耕服務治理能力與其他相關中間件研發。

前言

Spring Cloud Tencent 微服務開發框架自六月底正式對外宣發後,受到了許多開發者非常火熱的關注。不到一個月時間, Github Star 數就已突破 2000,超過 1000 名開發者加入我們的社羣,並有 20 多個開發者參與貢獻項目代碼,項目的熱門程度極大地超出我們的預期,同時也驗證了我們在最初宣發文章裏的觀點:Spring Boot + Spring Cloud 仍是當前使用相當廣泛開發框架

在這一個月時間裏,Spring Cloud Tencent 的關注者們最關心的問題就是,Spring Cloud Tencent 後續規劃是什麼?

在過去的一段時間,我們的主要精力聚焦在微服務領域最基本的服務治理原子能力,例如服務發現、動態配置、限流熔斷、路由等。Spring Cloud 其它套件基本上也侷限於這些基礎能力。但是企業真正在實踐 Spring Cloud 過程中,發現針對自身具體的業務場景,這些原子能力並不能直接提供解決方案,往往需要做二次開發、定製等。例如定製 Spring Cloud Gateway 的 Filter、增強 Feign、支持各種複雜的服務路由場景等。因此,開箱即用的業務通用解決方案對企業來説更具有價值。

綜上所述, Spring Cloud Tencent 後續重要的規劃之一就是在不斷夯實服務治理原子能力的基礎上,提供開箱即用的業務通用解決方案,從工具到方案的升級。

為此 Spring Cloud Tencent 新增了 spring-cloud-tencent-plugin-starts 模塊,在此模塊下實現不同業務場景的解決方案。現階段我們主要聚焦在精細化流量治理能力場景化方案上,並按照開發流程拆分為三個階段:

  1. 開發測試階段的多測試環境場景;
  2. 發佈階段的金絲雀發佈、藍綠髮布、全鏈路灰度等;
  3. 生產運行階段的單元化、AB測試等。

本期我們主要聊聊開發測試階段的多測試環境場景實戰,詳細介紹 Spring Cloud Tencent 實現多測試環境場景的方案。

一、基礎知識

1.1 什麼是測試環境路由

在實際的開發過程中,一個微服務架構系統下的不同微服務可能是由多個團隊進行開發與維護的,每個團隊只需關注所屬的一個或多個微服務,而各個團隊維護的微服務之間可能存在相互調用關係。如果一個團隊在開發其所屬的微服務,調試的時候需要驗證完整的微服務調用鏈路。此時需要依賴其他團隊的微服務,如何部署開發聯調環境就會遇到以下問題:

  1. 如果所有團隊都使用同一套開發聯調環境,那麼一個團隊的測試微服務實例無法正常運行時,會影響其他依賴該微服務的應用也無法正常運行。
  2. 如果每個團隊有單獨的一套開發聯調環境,那麼每個團隊不僅需要維護自己環境的微服務應用,還需要維護其他團隊環境的自身所屬微服務應用,效率大大降低。同時,每個團隊都需要部署完整的一套微服務架構應用,成本也隨着團隊數的增加而大大上升。

此時可以使用測試環境路由的架構來幫助部署一套運維簡單且成本較低開發聯調環境。測試環境路由是一種基於服務路由的環境治理策略,核心是維護一個穩定的基線環境作為基礎環境,測試環境僅需要部署需要變更的微服務。多測試環境有兩個基礎概念,如下所示:

  1. 基線環境(Baseline Environment): 完整穩定的基礎環境,是作為同類型下其他環境流量通路的一個兜底可用環境,用户應該儘量保證基線環境的完整性、穩定性。
  2. 測試環境(Feature Environment): 一種臨時環境,僅可能為開發/測試環境類型,測試環境不需要部署全鏈路完整的服務,而是僅部署本次有變更的服務,其他服務通過服務路由的方式複用基線環境服務資源。

部署完成多測試環境後,開發者可以通過一定的路由規則方式,將測試請求打到不同的測試環境,如果測試環境沒有相應的微服務處理鏈路上的請求,那麼會降級到基線環境處理。因此,開發者需要將開發新測試的微服務部署到對應的測試環境,而不需要更新或不屬於開發者管理的微服務則複用基線環境的服務,完成對應測試環境的測試。

雖然測試環境路由是一個相對成熟的開發測試環境解決方案,但是能夠開箱即用的生產開發框架卻不多,往往需要開發者二次開發相應的功能。因此需要一個相對完善的解決方案來幫助實現測試環境路由,簡化開發難度並提升開發效率。

1.2 服務路由

服務路由模型

服務路由抽象出最簡化的模型如下圖所示,解決的是 “哪些請求轉發到哪些實例” 的問題。細化來看,包含三個問題:

  1. 如何精確標識請求?
  2. 如何精確標識實例?
  3. 如何轉發?

(圖:服務路由模型示意圖)

在流量的微觀世界裏,統一通過標籤(屬性)來標識一個實體,例如請求有來源調用服務、目標環境標籤等,服務實例則有版本號、實例分組、環境分組等標籤。服務路由則是將滿足標籤匹配條件的請求轉發到滿足匹配條件的服務實例。所以服務路由的模型可拆解出如下的的專業術語:

  • 服務實例染色 (為服務實例設置標籤信息)
  • 量染色(為請求設置標籤信息)
  • 服務路由(根據路由策略,把請求轉發到目標實例)

服務實例標籤如何傳遞到調用方

服務實例註冊到註冊中心時,會帶上標籤信息。服務調用方從註冊中心獲取到服務實例信息就包含了實例的標籤信息。

標籤全鏈路透傳

有一類請求標籤數據需要在業務響應鏈路上一直傳遞,例如全鏈路追蹤裏的 TraceId 、測試環境路由的 FeatureEnv 標籤等。

服務路由和負載均衡的區別

服務路由和負載均衡都是解決選擇服務實例的問題。區別在於服務路由是從全量的服務實例中挑選出一批滿足路由規則的服務實例,而負載均衡則是從路由匹配之後的服務實例列表中挑選出一個適合處理請求的實例。

二、測試環境路由實現原理

2.1 方案總覽

測試環境路由的樣例實現以下圖為例,一共有兩個測試環境以及一個基線環境。流量從端到端會依次經過以下組件:App -> 網關 -> 用户中心 -> 積分中心 -> 活動中心。

(圖:測試環境路由示意圖)

根據上一節服務路由章節所述,為了達到測試環境路由的能力,開發工作需要做三件事情:

  1. 服務實例染色(標識實例屬於哪個測試環境)

  2. 流量染色(標識請求應該被轉發到哪個測試環境)

  3. 服務路由

    a 網關根據請求的目標測試環境標籤轉發到對應的目標測試環境的用户中心。

    b 服務調用時,優先轉發到同測試環境下的目標服務實例,如果同測試環境下沒有服務實例則轉發到基線環境。

以下三小節,將會詳細介紹這三部分的原理。

2.2 服務實例染色

在多測試環境的場景中,需要對每個測試環境部署的實例進行區分,因此需要在實例上打<featureenv=測試環境名> 的標籤。Spring Cloud Tencent 一共支持三種服務實例染色方式。

方式一:配置文件

在 Spring Boot 的 application.yml 配置文件裏配置以下內容即可實現染色:

spring:
  cloud:
    tencent:
      metadata:
        content:
          idc: shanghai
          env: f1

Spring Cloud Tencent 應用在啟動時,讀取配置文件並解析出 idc=shanghai 和 env=f1 標籤信息。

如果以上配置文件放在項目源碼裏,要實現不同的實例具有不同的標籤值則需要打不同包。可以通過以下兩種方式實現同一個運行包設置不同的標籤值:

  1. 通過 -D 啟動參數覆蓋,例如 -Dspring.cloud.tencent.metadata.content.idc=guangzhou
  2. 通過 Spring Boot 標準方式,把 application.yml 外掛本地磁盤上

方式二:環境變量

環境變量在容器場景下非常方便,Spring Cloud Tencent 約定了前綴為 SCT_METADATA_CONTENT_ 的環境變量為實例的標籤信息,例如:

  • SCT_METADATA_CONTENT_IDC=shanghai
  • SCT_METADATA_CONTENT_ENV=f1

Spring Cloud Tencent 應用在啟動時,自動會讀取環境變量並解析出 IDC=shanghai 和 ENV=f1 標籤信息。

方式三:自定義實現 SPI

前面兩種方式為 Spring Cloud Tencent 內置的方式,但是不一定符合每個生產項目的規範,因此 Spring Cloud Tencent 還提供了一種允許開發者自定義標籤 Provider 的方式。例如以下兩種實踐場景:

  1. 把實例標籤放到機器上的某一個配置文件裏,例如 /etc/metadata。
  2. 應用啟動時,調用公司的 CMDB 接口獲取元信息。

這種場景下,只要實現 InstanceMetadataProvider SPI 擴展即可。

2.3 流量染色

流量染色即為每個請求打上目標測試環境標籤,路由轉發時根據請求標籤匹配目標服務實例。而流量染色可以分為以下幾種方式:

方式一:靜態染色

2.2 小節介紹了可以為服務實例設置一系列的標籤信息,例如 idc=shanghai、env=f1 等。在有些場景下,期望所有經過當前實例的請求都帶上當前實例的標籤信息。例如經過 env=f1 的實例的請求都攜帶 env=f1 的標籤信息。

服務實例染色有三種方式,對應的定義哪些標籤需要作為請求標籤透傳到鏈路上也有三種方式,核心思想就是定義需要全鏈路傳遞的標籤鍵值對的鍵列表。

  1. 通過配置文件的 spring.cloud.tencent.metadata.content.transitive=["idc", "env"] 配置項指定
  2. 通過 SCT_METADATA_CONTENT_TRANSITIVE=IDC,ENV 環境變量指定
  3. 通過實現 InstanceMetadataProvider#getTransitiveMetadataKeys() 方法指定

方式二:動態染色

靜態染色是把服務實例的某些標籤作為請求標籤,服務實例標籤相對靜態,應用啟動後初始化一次之後就不再變更。但是在實際的應用場景下,不同的請求往往需要設置不同的標籤信息。此時則需要通過動態染色的能力。

為請求動態染色也非常簡單,只需增加以 X-Polaris-Metadata-Transitive- 為前綴的 HTTP 請求頭即可,例如:X-Polaris-Metadata-Transitive-featureenv=f1。這樣 featureenv=f1 就能夠作為請求標籤在鏈路上透傳。

方式三:網關流量染色

網關常常作為流量的入口或者中轉站。經過網關的請求,可以根據某些染色規則為請求增加標籤信息。例如滿足請求參數 uid=1000 請求打上 featureenv=f1 標籤。

網關流量染色是非常實用的能力,在 Spring Cloud Tencent 裏實現了非常靈活基於染色規則的 Spring Cloud Gateway 染色插件。例如以下染色規則可以實現為 uid=1000 的請求打上 featureenv=f1 標籤、uid=1001 的請求打上 featureenv=f2 標籤。更詳細的染色規則,可以參考文檔。

{
    "rules":[
        {
            "conditions":[
                {
                    "key":"${http.query.uid}",
                    "values":["1000"],
                    "operation":"EQUALS"
                }
            ],
            "labels":[
                {
                    "key":"featureenv",
                    "value":"f1"
                }
            ]
        },
        {
            "conditions":[
                {
                    "key":"${http.query.uid}",
                    "values":["1001"],
                    "operation":"EQUALS"
                }
            ],
            "labels":[
                {
                    "key":"featureenv",
                    "value":"f2"
                }
            ]
        }
    ]
}

同時 Spring Cloud Tencent 也預留了 TrafficStainer SPI ,用户可以實現自定義流量染色插件。

2.4 Spring Cloud Tencent 路由功能原理

北極星提供了非常完善的服務治理能力,上層的服務框架基於北極星原生 SDK 就能快速實現強大的服務治理能力。Spring Cloud Tencent 就是在北極星的基礎上實現了服務路由能力。

北極星服務路由原理

北極星服務路由實現原理並不複雜,如下圖所示,從註冊中心獲取到所有實例信息,再經過一系列的 RouterFilter 插件過濾出滿足條件的實例集合。

(圖:北極星服務路由執行鏈)

在多測試環境場景下主要用到了 MetadataRouter (元數據路由)插件,此插件核心能力是根據請求的標籤完全匹配服務實例的標籤。

例如請求有兩個標籤 key1=value1和 key2=value2,MetadataRouter 則會篩選出所有實例中包含同時滿足 key1=value1 和 key2=value2 的服務實例。在多測試環境場景下,Spring Cloud Tencent 缺省使用 featureenv 標籤,通過 featureenv 標籤篩選出屬於同一個測試環境的服務實例。

Spring Cloud Tencent 服務路由原理

Spring Cloud Tencent 實現路由核心分成兩個部分:

  1. 擴展 RestTemplate 、 Feign、SCG 獲取請求的標籤信息並塞到 RouterContext (路由信息上下文)裏。
  2. 擴展 Spring Cloud 負載均衡組件(Hoxton 版本之前為 Ribbon,2020版本之後為 Spring Cloud LoadBalancer),在擴展的實現裏調用北極星的服務路由 API 實現服務實例過濾。

擴展部分邏輯較為複雜,感興趣的讀者可以參考 spring-cloud-starter-tencent-polaris-router 模塊源碼。

三、測試環境路由用户操作指引

在上一節中詳細介紹了測試環境路由的實現原理,這一節則詳細介紹站在用户的視角需要操作的內容。

通過 Spring Cloud Tencent 實現流量的測試環境路由非常簡單,核心包含三步:

  1. 服務增加測試環境路由插件依賴
  2. 部署的實例打上環境標籤
  3. 為請求流量打上環境標籤

完成以上三個步驟即可。

3.1 添加測試環境路由插件依賴

Spring Cloud Tencent 中的 spring-cloud-tencent-featureenv-plugin 模塊閉環了測試環境路由全部能力,所有服務只需要添加該依賴即可引入測試環境路由能力。

3.2 服務實例打上環境標籤

spring-cloud-tencent-featureenv-plugin 默認以 featureenv 標籤作為匹配標籤,用户也可以通過系統內置的 system-feature-env-router-label=custom_feature_env_key 標籤來指定測試環境路由使用的標籤鍵。以下三種方式以默認的 featureenv 作為示例。

方式一:配置文件

在服務實例的配置文件中添加配置,如在 bootstrap.yml添加如下所示即可:

spring:
  cloud:
    tencent:
      metadata:
        content:
          featureenv: f1  # f1 替換為測試環境名稱

方式二:環境變量

在服務實例所在的操作系統中添加環境變量也可進行打標,例如:SCT_METADATA_CONTENT_featureenv=f1

方式三:SPI 方式

自定義實現 InstanceMetadataProvider#getMetadata() 方法的返回值裏裏包含 featureenv 即可。

基線環境標籤值

注意,基線環境部署的服務實例不需要設置 featureenv 標籤,表明其不屬於任何測試環境,才可在請求沒有匹配到對應測試環境的時候,匹配到基線環境。

3.3 流量染色

方式一:客户端染色 (推薦)

如下圖所示,在客户端發出的 HTTP 請求裏,新增 X-Polaris-Metadata-Transitive-featureenv=f1 請求頭即可實現染色。該方式是讓開發者在請求創建的時候根據業務邏輯進行流量染色。

(圖:客户端染色示意圖)

方式二:網關動態染色(推薦)

動態染色是開發者配置一定的染色規則,讓流量經過網關時自動染色,使用起來相當方便。例如把 uid=1 用户的請求都轉發到 f1 環境,把 uid=0 用户的請求都轉發到 f2 環境。只需要配置一條染色規則即可實現。

(圖:網關動態染色示意圖)

Spring Cloud Tencent 通過實現 Spring Cloud Gateway 的 GlobalFilter 來實現流量染色插件,開發者只需要添加 spring-cloud-tencent-gateway-plugin 依賴,並在配置文件中打開染色插件開關(spring.cloud.tencent.plugin.scg.staining.enabled=true)即可引入流量染色能力。

方式三:網關靜態染色

往請求中加入固定的 Header 是網關最常見的插件,如下圖所示。可以在每個環境部署一個網關,所有經過網關的請求都增加 X-Polaris-Metadata-Transitive-featureenv=f1 請求頭即可。此種方式需要每個環境部署網關,成本高,所以使用頻率相對較低。

(圖:網關靜態染色示意圖)

完成以上操作步驟即可實現測試環境路由,讀者可運行 Spring Cloud Tencent 下 polaris-router-featureenv-example 完整體驗。

四、總結

測試環境路由在微服務架構系統的開發階段是非常實用的功能,能夠大大降低測試環境的維護成本、資源成本,同時能夠極大的提高研發效率。通過操作指引的章節可以看出通過 Spring Cloud Tencent 實現測試環境路由非常簡單的,只需要部署的服務實例增加相應的環境標籤以及在請求頭中增加一個標籤即可。

業界常見的測試環境路由實現方案往往需要下發路由規則給鏈路上的服務,從而實現路由能力。但是通過北極星的元數據路由能力,整個方案裏無需下發任何路由規則,只需要在實例設置相應的標籤信息即可,操作成本非常低。

如果項目剛好使用 Spring Cloud Gateway 作為網關,那麼集成 Spring Cloud Tencent 裏的網關染色插件能夠進一步降低流量染色成本,客户端無需做任何事情,只需要配置網關染色規則即可實現流量染色。

目前 Spring Cloud Tencent 主要實現了微服務之間調用流量的測試環境路由能力,不涉及消息隊列、任務調度的測試環境路由能力。

\

五、歡迎共建

如果您所在項目正在使用 Spring Cloud 框架,並且

  • 沉澱出了非常實用的通用插件能力和場景化解決方案
  • 目前正遇到一些落地難題
  • 對 Spring Cloud Tencent 項目感興趣

非常歡迎您跟我們一起打磨更多實用且通用的能力,共建滿足各類實際生產場景使用的微服務開發框架。您的一個建議、Issue、Pull Request 甚至只是一個小小的 Star 都是對 Spring Cloud Tencent 社區極大的支持。

\

Github 地址:https://github.com/Tencent/spring-cloud-tencent