SpringCloud+Nacos+負載均衡器實現全鏈路灰度釋出實戰

👉 這是一個或許對你有用的社群
🐱 一對一交流/面試小冊/簡歷最佳化/求職解惑,歡迎加入芋道快速開發平臺知識星球。下面是星球提供的部分資料:
👉這是一個或許對你有用的開源專案
國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。
功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、CRM 等等功能:
  • Boot 倉庫:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 倉庫:https://gitee.com/zhijiantianya/yudao-cloud
  • 影片教程:https://doc.iocoder.cn
【國內首批】支援 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 雙版本 

概念

灰度釋出, 也叫金絲雀釋出。是指在黑與白之間,能夠平滑過渡的一種釋出方式。AB test就是一種灰度釋出方式,讓一部分使用者繼續用A,一部分使用者開始用B,如果使用者對B沒有什麼反對意見,那麼逐步擴大範圍,把所有使用者都遷移到B上面來。
灰度釋出可以保證整體系統的穩定,在初始灰度的時候就可以發現、調整問題,以保證其影響度,而我們平常所說的金絲雀部署也就是灰度釋出的一種方式。
具體到伺服器上,實際操作中還可以做更多控制,譬如說,給最初更新的10臺伺服器設定較低的權重、控制傳送給這10臺伺服器的請求數,然後逐漸提高權重、增加請求數。一種平滑過渡的思路, 這個控制叫做“流量切分”。
基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 影片教程:https://doc.iocoder.cn/video/

元件版本說明

我們這專案已經練習了兩年半了使用的版本不是很新,我這裡的Demo也會使用這個版本,有感情了,使用新版本的朋友自己調整一下就行,實現思路是一樣的只是這些框架原始碼可能會有變化。
  • spring-boot: 2.3.12.RELEASE
  • spring-cloud-dependencies: Hoxton.SR12
  • spring-cloud-alibaba-dependencies: 2.2.9.RELEASE
spring-cloud 對應版本關係圖
https://blog.csdn.net/weixin_44606481/article/details/131726688
基於 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/yudao-cloud
  • 影片教程:https://doc.iocoder.cn/video/

核心元件說明

  • 註冊中心: Nacos
  • 閘道器: SpringCloudGateway
  • 負載均衡器: Ribbon (使用SpringCloudLoadBalancer實現也是類似的)
  • 服務間RPC呼叫: OpenFeign

灰度釋出程式碼實現

要實現Spring Cloud專案灰度釋出技術方案有很多,重點在於服務發現,怎麼將灰度流量只請求到灰度服務,這裡我們會使用Nacos作為註冊中心和配置中心,核心就是利用Nacos的Metadata設定一個version值,在呼叫下游服務是透過version值來區分要呼叫那個版本,這裡會省略一些流程,文章末尾提供了原始碼地址需要自提。
程式碼設計結構
這個是demo專案,結構都按最簡單的來。

spring-cloud-gray-example // 父工程

   kerwin-common // 專案公共模組

   kerwin-gateway // 微服務閘道器

   kerwin-order // 訂單模組

      order-app // 訂單業務服務

   kerwin-starter // 自定義springboot starter模組

      spring-cloud-starter-kerwin-gray // 灰度釋出starter包 (核心程式碼都在這裡)

   kerwin-user // 使用者模組

      user-app // 使用者業務服務

      user-client // 使用者client(Feign和DTO)

核心包spring-cloud-starter-kerwin-gray結構介紹
入口Spring Cloud Gateway實現灰度釋出設計(一些基礎資訊類在下面)
在請求進入閘道器時開始對是否要請求灰度版本進行判斷,透過Spring Cloud Gateway的過濾器實現,在呼叫下游服務時重寫一個Ribbon的負載均衡器實現呼叫時對灰度狀態進行判斷。
存取請求灰度標記Holder(業務服務也是使用的這個)
使用ThreadLocal記錄每個請求執行緒的灰度標記,會在前置過濾器中將標記設定到ThreadLocal中。
publicclassGrayFlagRequestHolder

{

/**

     * 標記是否使用灰度版本

     * 具體描述請檢視 {

@link

 com.kerwin.gray.enums.GrayStatusEnum}

     */


privatestaticfinal

 ThreadLocal<GrayStatusEnum> grayFlag = 

new

 ThreadLocal<>();

publicstaticvoidsetGrayTag(final GrayStatusEnum tag)

{

        grayFlag.set(tag);

    }

publicstatic GrayStatusEnum getGrayTag()

{

return

 grayFlag.get();

    }

publicstaticvoidremove()

{

        grayFlag.remove();

    }

}

前置過濾器
在前置過濾器中會對請求是否要使用灰度版本進行判斷,並且會將灰度狀態列舉GrayStatusEnum設定到GrayRequestContextHolder中儲存這一個請求的灰度狀態列舉,在負載均衡器中會取出灰度狀態列舉判斷要呼叫那個版本的服務,同時這裡還實現了Ordered 介面會對閘道器的過濾器進行的排序,這裡我們將這個過濾器的排序設定為Ordered.HIGHEST_PRECEDENCE int的最小值,保證這個過濾器最先執行。
publicclassGrayGatewayBeginFilterimplementsGlobalFilterOrdered

{

@Autowired
private

 GrayGatewayProperties grayGatewayProperties;

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)

{

        GrayStatusEnum grayStatusEnum = GrayStatusEnum.ALL;

// 當灰度開關開啟時才進行請求頭判斷
if

 (grayGatewayProperties.getEnabled()) {

            grayStatusEnum = GrayStatusEnum.PROD;

// 判斷是否需要呼叫灰度版本
if

 (checkGray(exchange.getRequest())) {

                grayStatusEnum = GrayStatusEnum.GRAY;

            }

        }

        GrayFlagRequestHolder.setGrayTag(grayStatusEnum);

        ServerHttpRequest newRequest = exchange.getRequest().mutate()

                .header(GrayConstant.GRAY_HEADER, grayStatusEnum.getVal())

                .build();

        ServerWebExchange newExchange = exchange.mutate()

                .request(newRequest)

                .build();

return

 chain.filter(newExchange);

    }

/**

     * 校驗是否使用灰度版本

     */


privatebooleancheckGray(ServerHttpRequest request)

{

if

 (checkGrayHeadKey(request) || checkGrayIPList(request) || checkGrayCiryList(request) || checkGrayUserNoList(request)) {

returntrue

;

        }

returnfalse

;

    }

/**

     * 校驗自定義灰度版本請求頭判斷是否需要呼叫灰度版本

     */


privatebooleancheckGrayHeadKey(ServerHttpRequest request)

{

        HttpHeaders headers = request.getHeaders();

if

 (headers.containsKey(grayGatewayProperties.getGrayHeadKey())) {

            List<String> grayValues = headers.get(grayGatewayProperties.getGrayHeadKey());

if

 (!Objects.isNull(grayValues)

                    && grayValues.size() > 

0

                    && grayGatewayProperties.getGrayHeadValue().equals(grayValues.get(

0

))) {

returntrue

;

            }

        }

returnfalse

;

    }

/**

     * 校驗自定義灰度版本IP陣列判斷是否需要呼叫灰度版本

     */


privatebooleancheckGrayIPList(ServerHttpRequest request)

{

        List<String> grayIPList = grayGatewayProperties.getGrayIPList();

if

 (CollectionUtils.isEmpty(grayIPList)) {

returnfalse

;

        }

        String realIP = request.getHeaders().getFirst(

"X-Real-IP"

);

if

 (realIP == 

null

 || realIP.isEmpty()) {

            realIP = request.getRemoteAddress().getAddress().getHostAddress();

        }

if

 (realIP != 

null

 && CollectionUtils.contains(grayIPList.iterator(), realIP)) {

returntrue

;

        }

returnfalse

;

    }

/**

     * 校驗自定義灰度版本城市陣列判斷是否需要呼叫灰度版本

     */


privatebooleancheckGrayCiryList(ServerHttpRequest request)

{

        List<String> grayCityList = grayGatewayProperties.getGrayCityList();

if

 (CollectionUtils.isEmpty(grayCityList)) {

returnfalse

;

        }

        String realIP = request.getHeaders().getFirst(

"X-Real-IP"

);

if

 (realIP == 

null

 || realIP.isEmpty()) {

            realIP = request.getRemoteAddress().getAddress().getHostAddress();

        }

// 透過IP獲取當前城市名稱
// 這裡篇幅比較長不具體實現了,想要實現的可以使用ip2region.xdb,這裡寫死cityName = "本地"

        String cityName = 

"本地"

;

if

 (cityName != 

null

 && CollectionUtils.contains(grayCityList.iterator(), cityName)) {

returntrue

;

        }

returnfalse

;

    }

/**

     * 校驗自定義灰度版本使用者編號陣列(我們系統不會在閘道器獲取使用者編號這種方法如果需要可以自己實現一下)

     */


privatebooleancheckGrayUserNoList(ServerHttpRequest request)

{

        List<String> grayUserNoList = grayGatewayProperties.getGrayUserNoList();

if

 (CollectionUtils.isEmpty(grayUserNoList)) {

returnfalse

;

        }

returnfalse

;

    }

@Override
publicintgetOrder()

{

// 設定過濾器的執行順序,值越小越先執行
return

 Ordered.HIGHEST_PRECEDENCE;

    }

}

後置過濾器
後置過濾器是為了在呼叫完下游業務服務後在響應之前將 GrayFlagRequestHolder 中的 ThreadLocal 清除避免照成記憶體洩漏。
publicclassGrayGatewayAfterFilterimplementsGlobalFilterOrdered

{

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)

{

// 請求執行完必須要remore當前執行緒的ThreadLocal

        GrayFlagRequestHolder.remove();

return

 chain.filter(exchange);

    }

@Override
publicintgetOrder()

{

// 設定過濾器的執行順序,值越小越先執行
return

 Ordered.LOWEST_PRECEDENCE;

    }

}

全域性異常處理器
全域性異常處理器是為了處理異常情況下將 GrayFlagRequestHolder 中的 ThreadLocal 清除避免照成記憶體洩漏,如果在呼叫下游業務服務時出現了異常就無法進入後置過濾器。
publicclassGrayGatewayExceptionHandlerimplementsWebExceptionHandlerOrdered

{

@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex)

{

// 請求執行完必須要remore當前執行緒的ThreadLocal

        GrayFlagRequestHolder.remove();

        ServerHttpResponse response = exchange.getResponse();

if

 (ex 

instanceof

 ResponseStatusException) {

// 處理 ResponseStatusException 異常

            ResponseStatusException responseStatusException = (ResponseStatusException) ex;

            response.setStatusCode(responseStatusException.getStatus());

// 可以根據需要設定響應頭等
return

 response.setComplete();

        } 

else

 {

// 處理其他異常

            response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);

// 可以根據需要設定響應頭等
return

 response.setComplete();

        }

    }

@Override
publicintgetOrder()

{

// 設定過濾器的執行順序,值越小越先執行
return

 Ordered.HIGHEST_PRECEDENCE;

    }

}

自定義Ribbon負載均衡路由(業務服務也是使用的這個)
「灰度Ribbon負載均衡路由抽象類:」 這裡提供了兩個獲取服務列表的方法,會對GrayFlagRequestHolder 中儲存的當前執行緒灰度狀態列舉進行判斷,如果列舉值為GrayStatusEnum.ALL則響應全部服務列表不區分版本,如果列舉值為GrayStatusEnum.PROD則返回生產版本的服務列表,如果列舉值為GrayStatusEnum.GRAY則返回灰度版本的服務列表,版本號會在GrayVersionProperties 中配置,透過服務列表中在Nacos的metadata中設定的versionGrayVersionProperties的版本號進行匹配出對應版本的服務列表。
publicabstractclassAbstractGrayLoadBalancerRuleextendsAbstractLoadBalancerRule

{

@Autowired
private

 GrayVersionProperties grayVersionProperties;

@Value

(

"${spring.cloud.nacos.discovery.metadata.version}"

)

private

 String metaVersion;

/**

     * 只有已啟動且可訪問的伺服器,並對灰度標識進行判斷

     */


public List<Server> getReachableServers()

{

        ILoadBalancer lb = getLoadBalancer();

if

 (lb == 

null

) {

returnnew

 ArrayList<>();

        }

        List<Server> reachableServers = lb.getReachableServers();

return

 getGrayServers(reachableServers);

    }

/**

     * 所有已知的伺服器,可訪問和不可訪問,並對灰度標識進行判斷

     */


public List<Server> getAllServers()

{

        ILoadBalancer lb = getLoadBalancer();

if

 (lb == 

null

) {

returnnew

 ArrayList<>();

        }

        List<Server> allServers = lb.getAllServers();

return

 getGrayServers(allServers);

    }

/**

     * 獲取灰度版本服務列表

     */


protected List<Server> getGrayServers(List<Server> servers)

{

        List<Server> result = 

new

 ArrayList<>();

if

 (servers == 

null

) {

return

 result;

        }

        String currentVersion = metaVersion;

        GrayStatusEnum grayStatusEnum = GrayFlagRequestHolder.getGrayTag();

if

 (grayStatusEnum != 

null

) {

switch

 (grayStatusEnum) {

case

 ALL:

return

 servers;

case

 PROD:

                    currentVersion = grayVersionProperties.getProdVersion();

break

;

case

 GRAY:

                    currentVersion = grayVersionProperties.getGrayVersion();

break

;

            }

        }

for

 (Server server : servers) {

            NacosServer nacosServer = (NacosServer) server;

            Map<String, String> metadata = nacosServer.getMetadata();

            String version = metadata.get(

"version"

);

// 判斷服務metadata下的version是否於設定的請求版本一致
if

 (version != 

null

 && version.equals(currentVersion)) {

                result.add(server);

            }

        }

return

 result;

    }

}

「自定義輪詢演算法實現GrayRoundRobinRule:」 程式碼篇幅太長了這裡只擷取程式碼片段,我這裡是直接複製了Ribbon的輪詢演算法,將裡面獲取服務列表的方法換成了自定義AbstractGrayLoadBalancerRule 中的方法,其它演算法也可以透過類似的方式實現。
業務服務實現灰度釋出設計
自定義SpringMVC請求攔截器
自定義SpringMVC請求攔截器獲取上游服務的灰度請求頭,如果獲取到則設定到GrayFlagRequestHolder 中,之後如果有後續的RPC呼叫同樣的將灰度標記傳遞下去。
@SuppressWarnings

(

"all"

)

publicclassGrayMvcHandlerInterceptorimplementsHandlerInterceptor

{

@Override
publicbooleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception 

{

        String grayTag = request.getHeader(GrayConstant.GRAY_HEADER);

// 如果HttpHeader中灰度標記存在,則將灰度標記放到holder中,如果需要就傳遞下去
if

 (grayTag!= 

null

) {

            GrayFlagRequestHolder.setGrayTag(GrayStatusEnum.getByVal(grayTag));

        }

returntrue

;

    }

@Override
publicvoidpostHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception 

{
    }

@Override
publicvoidafterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception 

{

        GrayFlagRequestHolder.remove();

    }

}

自定義OpenFeign請求攔截器
自定義OpenFeign請求攔截器,取出自定義SpringMVC請求攔截器中設定到GrayFlagRequestHolder中的灰度標識,並且放到呼叫下游服務的請求頭中,將灰度標記傳遞下去。
publicclassGrayFeignRequestInterceptorimplementsRequestInterceptor

{

@Override
publicvoidapply(RequestTemplate template)

{

// 如果灰度標記存在,將灰度標記透過HttpHeader傳遞下去

        GrayStatusEnum grayStatusEnum = GrayFlagRequestHolder.getGrayTag();

if

 (grayStatusEnum != 

null

 ) {

            template.header(GrayConstant.GRAY_HEADER, Collections.singleton(grayStatusEnum.getVal()));

        }

    }

}

基礎資訊設計
這裡會定義一些基礎引數,比如是否開啟灰度還有什麼請求需要使用灰度版本等,為後續業務做準備。
  • 呼叫業務服務時設定的灰度統一請求頭
publicinterfaceGrayConstant

{

/**

     * 灰度統一請求頭

     */

    String GRAY_HEADER=

"gray"

;

}

  • 灰度版本狀態列舉
publicenum

 GrayStatusEnum {

    ALL(

"ALL"

,

"可以呼叫全部版本的服務"

),

    PROD(

"PROD"

,

"只能呼叫生產版本的服務"

),

    GRAY(

"GRAY"

,

"只能呼叫灰度版本的服務"

);

    GrayStatusEnum(String val, String desc) {

this

.val = val;

this

.desc = desc;

    }

private

 String val;

private

 String desc;

public String getVal()

{

return

 val;

    }

publicstatic GrayStatusEnum getByVal(String val)

{

if

(val == 

null

){

returnnull

;

        }

for

 (GrayStatusEnum value : values()) {

if

(value.val.equals(val)){

return

 value;

            }

        }

returnnull

;

    }

}

  • 閘道器灰度配置資訊類
@Data
@Configuration
@RefreshScope
@ConfigurationProperties

(

"kerwin.tool.gray.gateway"

)

publicclassGrayGatewayProperties

{

/**

     * 灰度開關(如果開啟灰度開關則進行灰度邏輯處理,如果關閉則走正常處理邏輯)

     * PS:一般在灰度釋出測試完成以後會將線上版本都切換成灰度版本完成全部升級,這時候應該關閉灰度邏輯判斷

     */


private

 Boolean enabled = 

false

;

/**

     * 自定義灰度版本請求頭 (透過grayHeadValue來匹配請求頭中的值如果一致就去呼叫灰度版本,用於公司測試)

     */


private

 String grayHeadKey=

"gray"

;

/**

     * 自定義灰度版本請求頭匹配值

     */


private

 String grayHeadValue=

"gray-996"

;

/**

     * 使用灰度版本IP陣列

     */


private

 List<String> grayIPList = 

new

 ArrayList<>();

/**

     * 使用灰度版本城市陣列

     */


private

 List<String> grayCityList = 

new

 ArrayList<>();

/**

     * 使用灰度版本使用者編號陣列(我們系統不會在閘道器獲取使用者編號這種方法如果需要可以自己實現一下)

     */


private

 List<String> grayUserNoList = 

new

 ArrayList<>();

}

  • 全域性版本配置資訊類
@Data
@Configuration
@RefreshScope
@ConfigurationProperties

(

"kerwin.tool.gray.version"

)

publicclassGrayVersionProperties

{

/**

     * 當前線上版本號

     */


private

 String prodVersion;

/**

     * 灰度版本號

     */


private

 String grayVersion;

}

  • 全域性自動配置類
@Configuration
// 可以透過@ConditionalOnProperty設定是否開啟灰度自動配置 預設是不載入的
@ConditionalOnProperty

(value = 

"kerwin.tool.gray.load"

,havingValue = 

"true"

)

@EnableConfigurationProperties

(GrayVersionProperties

.

class

)

publicclassGrayAutoConfiguration

{

@Configuration

(proxyBeanMethods = 

false

)

@ConditionalOnClass

(value = GlobalFilter

.

class

)

    @

EnableConfigurationProperties

(

GrayGatewayProperties

.

class

)

staticclassGrayGatewayFilterAutoConfiguration

{

@Bean
public GrayGatewayBeginFilter grayGatewayBeginFilter()

{

returnnew

 GrayGatewayBeginFilter();

        }

@Bean
public GrayGatewayAfterFilter grayGatewayAfterFilter()

{

returnnew

 GrayGatewayAfterFilter();

        }

@Bean
public GrayGatewayExceptionHandler grayGatewayExceptionHandler()

{

returnnew

 GrayGatewayExceptionHandler();

        }

    }

@Configuration

(proxyBeanMethods = 

false

)

@ConditionalOnClass

(value = WebMvcConfigurer

.

class

)

staticclassGrayWebMvcAutoConfiguration

{

/**

         * Spring MVC 請求攔截器

         * 

@return

 WebMvcConfigurer

         */


@Bean
public WebMvcConfigurer webMvcConfigurer()

{

returnnew

 WebMvcConfigurer() {

@Override
publicvoidaddInterceptors(InterceptorRegistry registry)

{

                    registry.addInterceptor(

new

 GrayMvcHandlerInterceptor());

                }

            };

        }

    }

@Configuration
@ConditionalOnClass

(value = RequestInterceptor

.

class

)

staticclassGrayFeignInterceptorAutoConfiguration

{

/**

         * Feign攔截器

         * 

@return

 GrayFeignRequestInterceptor

         */


@Bean
public GrayFeignRequestInterceptor grayFeignRequestInterceptor()

{

returnnew

 GrayFeignRequestInterceptor();

        }

    }

}

專案執行配置

這裡我會啟動五個服務,一個閘道器服務、一個使用者服務V1版本、一個訂單服務V1版本、一個使用者服務V2版本、一個訂單服務V2版本,來演示灰度釋出效果。
PS:Nacos的名稱空間我這裡叫spring-cloud-gray-example可以自己建立一個也可以換成自己的名稱空間,原始碼裡面配置都是存在的,有問題看原始碼就行
配置Nacos全域性配置檔案(common-config.yaml)
所有服務都會使用到這個配置

kerwin:

  tool:

    gray:

      # 配置是否載入灰度自動配置類,如果不配置那麼預設不載入

      load: 

true

      # 配置生產版本和灰度版本號

      version:

        prodVersion: V1

        grayVersion: V2
# 配置Ribbon呼叫user-app和order-app服務時使用我們自定義灰度輪詢演算法

user-app:

  ribbon:

    NFLoadBalancerRuleClassName: com.kerwin.gray.loadbalancer.GrayRoundRobinRule

order-app:

  ribbon:

    NFLoadBalancerRuleClassName: com.kerwin.gray.loadbalancer.GrayRoundRobinRule

配置閘道器Nacos配置檔案(gateway-app.yaml)

kerwin:

  tool:

    gray:

      gateway:

        # 是否開啟灰度釋出功能

        enabled: 

true

        # 自定義灰度版本請求頭

        grayHeadKey: gray

        # 自定義灰度版本請求頭匹配值

        grayHeadValue: gray-

996

        # 使用灰度版本IP陣列

        grayIPList:

          - 

'127.0.0.1'

        # 使用灰度版本城市陣列

        grayCityList:

          - 本地

啟動閘道器服務
閘道器服務啟動一個就行,直接Debug啟動即可,方便除錯原始碼
啟動業務服務V1 和 V2版本(使用者服務和訂單服務都用這種方式啟動)
先直接Debug啟動會在IDEA這個位置看到一個對應啟動類名稱的資訊
點選Edit編輯這個啟動配置
複製一個對應啟動配置作為V2版本,自己將Name改成自己能區分的即可
配置啟動引數,第一步點選Modify options 然後第二步將Add VM options勾選上,第三步填寫對應服務的啟動埠和Nacos的metadata.version,我這裡使用者服務V1版本配置為-Dserver.port=7201-Dspring.cloud.nacos.discovery.metadata.version=V1,使用者服務V2版本配置為-Dserver.port=7202-Dspring.cloud.nacos.discovery.metadata.version=V2,訂單服務配置類似,配置好後點Apply。
最後啟動好的服務資訊

灰度效果演示

原始碼中的user-app提供了一個獲取使用者資訊的介面並且會攜帶當前服務的埠和版本資訊,order-app服務提供了一個獲取訂單資訊的介面,會去遠端呼叫user-app獲取訂單關聯的使用者資訊,並且也會攜帶當前服務的埠和版本資訊響應。
場景一(關閉灰度開關:不區分呼叫服務版本)
關閉灰度開關有兩個配置可以實現
1、在專案啟動之前修改Nacos全域性配置檔案中的kerwin.tool.gray.load 配置是否載入灰度自動配置類,只要配置不為true就不會載入整個灰度相關類
2、關閉閘道器灰度開關,修改閘道器Nacos配置檔案中的kerwin.tool.gray.gateway.enabled ,只要配置不為true就不會進行灰度判斷。
呼叫演示
這裡呼叫不一定就是Order服務版本為V1 User服務版本也為V1,也有可能Order服務版本為V1 User服務版本也為V2.
  • 第一次呼叫,Order服務版本為V1,User服務版本也為V1
  • 第二次呼叫,Order服務版本為V2,User服務版本也為V2
場景二(開啟灰度開關:只調用生產版本)
修改閘道器Nacos配置檔案中的kerwin.tool.gray.gateway.enabled 設定為true,其它灰度IP陣列和城市陣列配置匹配不上就行,這樣怎麼呼叫都是V1版本,因為在GrayVersionProperties版本配置中設定的生產版本就是為V1灰度版本為V2。
場景三(開啟灰度開關:透過請求頭、ip、城市匹配呼叫灰度版本)
這裡透過請求頭測試,攜帶請求頭gray=gray-996訪問閘道器那麼流量就會都進入灰度版本V2。

原始碼

https://gitee.com/kerwin_code/spring-cloud-gray-example

存在問題

1、如果專案中使用到了分散式任務排程那怎麼區分灰度版本
這裡其實挺好解決的,就拿xxl-job來說,註冊不同的執行器就行,在釋出灰度版本時註冊到灰度版本的執行器即可。
2、如果專案中使用的了MQ我們收發訊息怎麼控制灰度
這裡和解決分散式任務排程思想是一樣的灰度版本的服務傳送訊息的時候投遞到另外一個MQ的服務端,就是弄兩套MQ服務端,生產的服務使用生產的MQ,灰度釋出使用灰度的MQ
3、這裡整個實現流程不是很複雜,但也是很沒必要,只是提供一種實現方案可以參考
其實透過Nginx + Lua指令碼方式直接路由閘道器,然後給灰度整套服務都使用一個Nacos灰度的名稱空間,生產的使用生產的名稱空間,這樣就能將兩套服務都隔離了,分散式任務排程、MQ等配置都可以獨立在自己名稱空間的配置檔案中豈不美哉

歡迎加入我的知識星球,全面提升技術能力。
👉 加入方式,長按”或“掃描”下方二維碼噢
星球的內容包括:專案實戰、面試招聘、原始碼解析、學習路線。
文章有幫助的話,在看,轉發吧。
謝謝支援喲 (*^__^*)

相關文章