微服務生態的灰度發佈如何實現? 前言 實現原理一 引入依賴 規則說明 外部參數Header 總結

享學課堂特邀作者:老顧
轉載請聲明出處

前言

相信很多小夥伴們都聽說過灰度發佈,但是不一定知道如何實現?今天我們就介紹一下基本原理,以及提供代碼實現給小夥伴們。

灰度概念

即原來的生產環境是1.0版本,那現在我們需要升級到2.0版本,但是我們需要驗證2.0版本,在生產環境不會出問題,而且要穩定後,才能夠完全切換爲2.0

小夥伴們就會問,2.0版本應該我們測試人員 測試了啊,肯定沒有問題啊。升級到2.0,直接升級就行了啊。

這個是不對的,因爲生產環境有很多場景是獨有的,如用戶流量,數據量等。所以必須要驗證2.0的穩定性;升級步驟如下:

1)100%的用戶流量打到1.0版本上面;但測試人員可以通過測試工具指定線上2.0版本,即測試人員請求的是2.0版本。
2)測試初步驗證後,可以先分5%的用戶流量到2.0版本,95%的用戶流量還是在1.0版本
3)查看2.0的穩定情況,分階段的分配用戶流量
4)完成切換到2.0版本,下線1.0版本;在下線1.0版本的時候不同的方案 會遇到不同的坑

實現原理一

老顧這個灰度的框架是建立在springcloud alibaba生態的註冊中心和配置中心都是使用的是nacos;我們先來看看整個系統架構,在升級狀態是什麼情況:

在spring cloud中,不管是網關 -> 服務A -> 服務B -> 服務C整個請求,網關是怎麼知道有哪些服務A的?服務A是怎麼知道有哪些服務B?服務B有哪些服務C?

這個是因爲我們的所有服務都註冊到了nacos註冊中心裏面,然後每個服務實例會通過5秒的心跳去請求nacos,獲取到nacos裏面的註冊信息。

這個就是很重要的一個點,服務信息的同步;我們可以重寫每個服務實例去拉取服務信息的時候,做一些過濾處理。舉個例子,我們怎麼保證服務A(1.0)只訪問服務B(1.0)。

服務A(1.0)在調用服務B時,需要去拉取服務B的服務實例信息,當我們發現服務B中有2.0版本時,就直接過濾掉;1.0才同步到本地緩存;通過這原理,就是保證了服務A(1.0)實例,只能訪問服務B(1.0)

那怎麼重寫拉取服務呢?就是繼承NacosServerList重寫getInitialListOfServers、getUpdatedListOfServers方法,直接上源代碼。

實現原理二

只實現過濾掉不需要的版本,是不夠的;因爲我們有另一個需求,就是流量權重;即服務A可以訪問服務B(1.0),也就是訪問服務B(2.0);只是流量權重佔比不一樣而已;那怎麼實現呢?

那就要用到我們客戶端client的rabbion組件了,這個組件主要用來調用服務實例的,而是可以實現負載均衡;那我們就可以重寫負載均衡算法,實現自定義的流量權重這個需求。

具體實現就是繼承ZoneAvoidanceRule

重寫Server choose(Object key),直接上代碼

public class ZoneAvoidanceRuleDecorator extends ZoneAvoidanceRule {
    。。。。。。
    @Override
    public Server choose(Object key) {
      //todo 選擇的邏輯
    }
}

知道了基本的原理後,就可以直接去寫了。

實現原理三

另一個需求,就是我們需要考慮到在線動態的改變灰度路由的規則,而不是每次改變了規則,需要重新啓動服務,這個就不對了。

這個的實現可以考慮利用nacos自身的功能。去訂閱nacos的配置中心的變化,從而達到動態更新。

案例

因爲篇幅原因,老顧這裏只介紹一些原理,具體源代碼,可以到git上面去獲取,要給個star哦。

https://gitee.com/gujiachun/gray

引入依賴

1、微服務項目需引入gray-plugin-framework-starter-service項目。

<dependency>
   <groupId>com.rainbow.gray</groupId>
   <artifactId>gray-plugin-framework-starter-service</artifactId>
   <version>1.0.0-SNAPSHOT</version>
</dependency>

2、網關項目需引入gray-plugin-framework-starter-gateway項目。

<dependency>
   <groupId>com.rainbow.gray</groupId>
   <artifactId>gray-plugin-framework-starter-gateway</artifactId>
   <version>1.0.0-SNAPSHOT</version>
</dependency>

3、本組件推薦使用遠程配置的方法,配置rule規則,現在只支持nacos配置中心。

4、組件支持全局訂閱,或局部訂閱。

全局訂閱即:DataId = group1,Group=group1;即DataId也爲group名稱,這樣每個微服務以及網關都只訂閱的是同一個rule規則。

局部訂閱即:DataId = 服務名稱,Group=group1;即DataID是服務名稱,即只有這個服務訂閱了rule規則;其他服務不適用。

(推薦全局訂閱,因爲灰度發佈一般針對所有的服務生效;而且規則裏面可以細化到每個服務的規則)

規則說明

使用說明

1、配置nacos,在bootstrap.properties中配置nacos註冊中心

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
spring.cloud.nacos.discovery.namespace=sit

2、在bootstrap.properties中組件需要的配置。

nacos.server-addr=${spring.cloud.nacos.discovery.server-addr}
nacos.username=${spring.cloud.nacos.discovery.username}
nacos.password=${spring.cloud.nacos.discovery.password}
nacos.plugin.namespace=${spring.cloud.nacos.discovery.namespace}

3、在application.properties 配置元數據,非常重要,這個就是每個服務不同的版本version,地域region,環境env,區zone相關的配置

spring.cloud.nacos.discovery.metadata.group=example-service-group
spring.cloud.nacos.discovery.metadata.version=1.0
spring.cloud.nacos.discovery.metadata.region=dev
spring.cloud.nacos.discovery.metadata.env=env1
spring.cloud.nacos.discovery.metadata.zone=zone1

4、親和性

啓動和關閉可用區親和性,即同一個可用區的服務才能調用,同一個可用區的條件是調用端實例和提供端實例的元數據Metadata的zone配置值必須相等。缺失則默認爲false

spring.application.zone.affinity.enabled=true

啓動和關閉可用區親和性失敗後的路由,即調用端實例沒有找到同一個可用區的提供端實例的時候,當開關打開,可路由到其它可用區或者不歸屬任何可用區,當開關關閉,則直接調用失敗。缺失則默認爲true

spring.application.zone.route.enabled=true

外部參數Header

通過前端(Postman)方式傳入灰度路由策略,來代替配置中心方式,傳遞全鏈路路由策略。這樣就可以實現由測試人員自行選擇走哪些路徑。

注意:當配置中心和外部參數都配置後,會先從配置中心的規則會先過濾執行,然後外部參數再過濾的原則

-版本匹配策略,Header格式如下任選一個

n-d-version=1.0
n-d-version={"discovery-guide-service-a":"2.0", "discovery-guide-service-b":"2.0"}

-版本權重策略,Header格式如下任選一個

n-d-version-weight=1.0=90;1.1=10
n-d-version-weight={"discovery-guide-service-a":"1.0=90;1.1=10", "discovery-guide-service-b":"1.0=90;1.1=10"}

-區域匹配策略,Header格式如下任選一個

n-d-region=qa
n-d-region={"discovery-guide-service-a":"qa", "discovery-guide-service-b":"qa"}

-區域權重策略,Header格式如下任選一個

n-d-region-weight=dev=99;qa=1
n-d-region-weight={"discovery-guide-service-a":"dev=99;qa=1", "discovery-guide-service-b":"dev=99;qa=1"}

-IP地址和端口匹配策略,Header格式如下任選一個

n-d-address={"discovery-guide-service-a":"127.0.0.1:3001", "discovery-guide-service-b":"127.0.0.1:4002"}
n-d-address={"discovery-guide-service-a":"127.0.0.1", "discovery-guide-service-b":"127.0.0.1"}
n-d-address={"discovery-guide-service-a":"3001", "discovery-guide-service-b":"4002"}

-環境隔離下動態環境匹配策略

n-d-env=env1

-服務下線實時性的流量絕對無損,全局唯一ID屏蔽策略

n-d-id-blacklist=e92edde5-0153-4ec8-9cbb-b4d3f415aa33;af043384-c8a5-451e-88f4-457914e8e3bc

-服務下線實時性的流量絕對無損,IP地址和端口屏蔽策略

n-d-address-blacklist=192.168.43.101:1201;192.168.*.102;1301

當外界傳值Header的時候,網關也設置並傳遞同名的Header,需要決定哪個Header傳遞到後邊的服務去。需要通過如下開關做控制:

# 當外界傳值Header的時候,網關也設置並傳遞同名的Header,需要決定哪個Header傳遞到後邊的服務去。如果下面開關爲true,以網關設置爲優先,否則以外界傳值爲優先。缺失則默認爲true
spring.application.strategy.gateway.header.priority=false
# 當以網關設置爲優先的時候,網關未配置Header,而外界配置了Header,仍舊忽略外界的Header。缺失則默認爲true
spring.application.strategy.gateway.original.header.ignored=true
# 當外界傳值Header的時候,網關也設置並傳遞同名的Header,需要決定哪個Header傳遞到後邊的服務去。如果下面開關爲true,以網關設置爲優先,否則以外界傳值爲優先。缺失則默認爲true
spring.application.strategy.zuul.header.priority=false
# 當以網關設置爲優先的時候,網關未配置Header,而外界配置了Header,仍舊忽略外界的Header。缺失則默認爲true
spring.application.strategy.zuul.original.header.ignored=true

總結

整體灰度的功能還是挺多,不過實現的原理就是上面介紹的原理;這裏也要感謝開源社區的nepxion discovery開源項目,借鑑了設計思路,以及一些代碼設計,簡化了實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章