如何解決 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 地址:http://github.com/Tencent/spring-cloud-tencent