Spring cloud架構中利用zuul網關實現灰度發布功能


 

 

藍綠發布、金絲雀發布(灰度發布)、AB測試

首先,了解下這幾種發布方式的基礎概念。

目前常見的發布策略有藍綠發布、金絲雀發布(灰度發布)、AB測試這幾種,在國內的開發者中,對這幾個概念有獨立的理解。藍綠發布通常被大家成為熱部署;金絲雀發布在國內的名頭完全被他的變種發布方式蓋過了,主要是灰度發布與AB測試,下面來詳細的為大家解釋一下他們之間的異同。

藍綠發布

在發布的過程中用戶無感知服務的重啟,通常情況下是通過新舊版本並存的方式實現,也就是說在發布的流程中,新的版本和舊的版本是相互熱備的,通過切換路由權重的方式(非0即100)實現不同的應用的上線或者下線。

金絲雀發布

通過在線上運行的服務中,新加入少量的新版本的服務,然后從這少量的新版本中快速獲得反饋,根據反饋決定最后的交付形態。

灰度發布

灰度發布是通過切換線上並存版本之間的路由權重,逐步從一個版本切換為另一個版本的過程。雖然有很多人包括專業大牛認為灰度發布與金絲雀發布是等同的,但是在具體的操作和目的上面個還是有些許差別的。金絲雀發布更傾向於獲取快速的反饋,而灰度發布更傾向於從一個版本到另一個版本平穩的切換。

AB測試

AB測試和灰度發布非常像,但是從發布的目的上,可以簡單的區分灰度發布與AB測試,AB測試側重的是從A版本或者B版本之間的差異,並根據這個結果進行決策。最終選擇一個版本進行部署。因此和灰度發布相比,AB測試更傾向於去決策,和金絲雀發布相比,AB測試在權重和流量的切換上更靈活。

在Spring cloud架構體系中基於eureka、ribbon實現灰度發布,是本篇要講的知識。

我們要發布版本了,在不確定正確性的情況下,我們選擇先部分節點升級,然后讓一些特定的流量進入到這些新節點,完成測試后再全量發布。

我們知道,在eureka中注冊各個服務后,如果一個服務有多個實例,那么默認會走ribbon的軟負載均衡來進行分發請求。

我們要完成灰度發布,要做的就是修改ribbon的負載策略(rule),通過一些特定的標識,譬如我們可以選擇header里帶有foo=1的全部路由到金絲雀服務上,其他的還走原來的老版本。或者可以設置個比重,雖然roll個小於4的正數,將等於1的路由到金絲雀,這樣就會有1/4的請求到達金絲雀。諸如此類,我們可以定制各種規則來進行灰度測試。

在SpringCloud體系中,完成這件事,模式比較固定,就是根據eureka的metadata進行自定義元數據,然后修改ribbon的Rule規則。

使用很簡單,我們直接上例子,注意我這里只發出來目標服務和zuul的代碼,eureka的就不放了。eureka很簡單,就是一個eureka server項目,什么也沒有。

我們的目標服務是User,在User的application.yml里,由於我要啟動2個,所以使用不同的端口

application.yml

server: port: 8888 eureka: instance: prefer-ip-address: true metadata-map: lancher: 2 client: service-url: defaultZone: http://localhost:10000/eureka/ application-dev.yml server: port: 8889 eureka: instance: metadata-map: lancher: 1

就是那個metadata-map元數據,這是一個map,里面就自定義一些key-value鍵值對。將來匹配時就用這個鍵值對。
然后分別啟動這兩個實例,啟動后就有兩個user注冊到了eureka。

zuul配置:

在zuul項目里添加依賴,https://github.com/jmnarloch/ribbon-discovery-filter-spring-cloud-starter

<dependency> <groupId>io.jmnarloch</groupId> <artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId> <version>2.1.0</version> </dependency>

這個就是做ribbon的Rule的。

package com.example.zuul_route; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import io.jmnarloch.spring.cloud.ribbon.support.RibbonFilterContextHolder; import org.springframework.context.annotation.Configuration; import javax.servlet.http.HttpServletRequest; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.*; /** * @author log4geek.cc wrote on 2018/11/17. */ @Configuration public class PreFilter extends ZuulFilter { @Override public int filterOrder() { return PRE_DECORATION_FILTER_ORDER - 1; } @Override public String filterType() { return PRE_TYPE; } @Override public boolean shouldFilter() { RequestContext ctx = RequestContext.getCurrentContext(); // a filter has already forwarded // a filter has already determined serviceId return !ctx.containsKey(FORWARD_TO_KEY) && !ctx.containsKey(SERVICE_ID_KEY); } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); if (request.getParameter("foo") != null) { // put the serviceId in `RequestContext` RibbonFilterContextHolder.getCurrentContext() .add("lancher", "1"); } else { RibbonFilterContextHolder.getCurrentContext() .add("lancher", "2"); } return null; } }

這個是zuul的filter,關鍵代碼在run方法,

RibbonFilterContextHolder.getCurrentContext() .add("lancher", "1");

這句話就代表將請求路由到metadata-map里lancher為1的那個服務。
整個過程很簡單,我們就可以在這里定制各種規則了,把符合什么條件的請求,只發送到某個實例


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM