SpringCloud的全鏈路灰度發(fā)布方案詳解
1.什么是灰度發(fā)布
灰度發(fā)布(又名金絲雀發(fā)布)是指在黑與白之間,能夠平滑過渡的一種發(fā)布方式。
在上面可以進行A/B測試, 即讓一部分用戶繼續(xù)用產(chǎn)品特性A, 一部分用戶使用產(chǎn)品特性B, 如果用戶B沒有反對意見的話, 那么會逐漸擴大范圍, 把用戶都往特性B 上改。
灰度發(fā)布可以保證系統(tǒng)的穩(wěn)定性, 在初始灰度的時候可以發(fā)現(xiàn), 調(diào)整問題, 保證其影響度。
2.什么是全鏈路灰度發(fā)布
網(wǎng)關灰度發(fā)布:
網(wǎng)關通過灰度標記路由到文章服務B, 至于從文章服務B到評論服務是通過openfeign內(nèi)部調(diào)用的, 默認無法實現(xiàn)灰度標記grayTag的透傳, 因此文章服務B 最終調(diào)用的是評論服務A, 并不是評論服務B。
全鏈路灰度發(fā)布需要實現(xiàn)的是:
- 網(wǎng)關通過灰度標記將部分流量轉(zhuǎn)發(fā)給文章服務B
- 文章服務B能夠?qū)崿F(xiàn)灰度標記grayTag的透傳,最終調(diào)用評論服務B
全鏈路灰度發(fā)布需要實現(xiàn)兩個點:
- 網(wǎng)關路由轉(zhuǎn)發(fā)實現(xiàn)灰度發(fā)布
- 服務內(nèi)部通過openFeign調(diào)用實現(xiàn)灰度發(fā)布(透傳灰度標記grayTag)。
3.網(wǎng)關層的灰度路由轉(zhuǎn)發(fā)
Ribbon+Spring Cloud Gateway 進行改造負載均衡策略實現(xiàn)灰度發(fā)布.
- 在網(wǎng)關的全局過濾器中根據(jù)業(yè)務規(guī)則給流量打上灰度標記
- 將灰度標記放入請求頭中,傳遞給下游服務
- 改造Ribbon負載均衡策略,根據(jù)流量標記從注冊中心獲取灰度服務
- 請求路由轉(zhuǎn)發(fā)
根據(jù)什么條件打上灰度標記
這個需要根據(jù)實際的業(yè)務需要,比如根據(jù)用戶所在的地區(qū)、使用客戶端類型、隨機截取流量
這里我將直接使用一個標記grayTag,只要客戶端請求頭中攜帶了這個參數(shù),并且設置為true,則走灰度發(fā)布邏輯。
grayTag=true
為什么要在請求頭中添加灰度標記傳遞給下游服務
這一步非常關鍵,實現(xiàn)灰度標記透傳給下游服務的關鍵,將灰度標記放在請求頭中,下游服務只需要從請求頭中獲取灰度標記便知道是否是灰度發(fā)布,這個和令牌中繼一個原理。
灰度標記如何請求隔離
Spring MVC中的每個請求都是開啟一個線程進行處理,因此可以將灰度標記放置在 ThreadLocal
中進行線程隔離。
如何知道注冊中心的服務哪個是灰度服務
Nacos支持在服務中配置一些元數(shù)據(jù),可以將灰度標記配置在元數(shù)據(jù)中,這樣就能區(qū)分哪些是灰度服務,哪些是正常服務。
如何針對特定的服務進行灰度發(fā)布
我們知道網(wǎng)關路由中配置的服務很多,如何只針對文章服務進行灰度發(fā)布
只需要將自定義的Ribbon灰度發(fā)布規(guī)則只對文章服務生效 利用注解RibbonClients, 指定特定微服務
@RibbonClients(value ={ //只對文章服務進行灰度發(fā)布 @RibbonClient(value = "article-server",configuration = GrayRuleConfig.class) } ) @SpringBootApplication public class GatewayApplication { }
@RibbonClients其中有一個屬性defaultConfiguration,一旦使用這個屬性,那么灰度發(fā)布的策略對網(wǎng)關路由中配置的所有服務都將生效。
具體如何實現(xiàn)
網(wǎng)關中首先需要定義一個全局過濾器:
public class GlobalGrayFilter implements GlobalFilter{ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //① 解析請求頭,查看是否存在灰度發(fā)布的請求頭信息,如果存在則將其放置在ThreadLocal中 HttpHeaders headers = exchange.getRequest().getHeaders(); if (headers.containsKey(GrayConstant.GRAY_HEADER)){ String gray = headers.getFirst(GrayConstant.GRAY_HEADER); if (StrUtil.equals(gray,GrayConstant.GRAY_VALUE)){ //②設置灰度標記 GrayRequestContextHolder.setGrayTag(true); } } //③ 將灰度標記放入請求頭中 ServerHttpRequest tokenRequest = exchange.getRequest().mutate() //將灰度標記傳遞過去 .header(GrayConstant.GRAY_HEADER,GrayRequestContextHolder.getGrayTag().toString()) .build(); ServerWebExchange build = exchange.mutate().request(tokenRequest).build(); return chain.filter(build); } }
GrayRule:
public class GrayRule extends ZoneAvoidanceRule { @Override public void initWithNiwsConfig(IClientConfig clientConfig) { } @Override public Server choose(Object key) { try { //從ThreadLocal中獲取灰度標記 boolean grayTag = GrayRequestContextHolder.getGrayTag().get(); //獲取所有可用服務 List<Server> serverList = this.getLoadBalancer().getReachableServers(); //灰度發(fā)布的服務 List<Server> grayServerList = new ArrayList<>(); //正常的服務 List<Server> normalServerList = new ArrayList<>(); for(Server server : serverList) { NacosServer nacosServer = (NacosServer) server; //從nacos中獲取元素劇進行匹配 if(nacosServer.getMetadata().containsKey(GrayConstant.GRAY_HEADER) && nacosServer.getMetadata().get(GrayConstant.GRAY_HEADER).equals(GrayConstant.GRAY_VALUE)) { grayServerList.add(server); } else { normalServerList.add(server); } } //如果被標記為灰度發(fā)布,則調(diào)用灰度發(fā)布的服務 if(grayTag) { return originChoose(grayServerList,key); } else { return originChoose(normalServerList,key); } } finally { //清除灰度標記 GrayRequestContextHolder.remove(); } } private Server originChoose(List<Server> noMetaServerList, Object key) { Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(noMetaServerList, key); if (server.isPresent()) { return server.get(); } else { return null; } } }
- 獲取灰度標記
- 從Nacos注冊中心獲取灰度服務和正常服務
- 根據(jù)灰度標記去判斷,如果灰度發(fā)布則選擇特定的灰度服務進行轉(zhuǎn)發(fā)
定義一個配置類,注入改造的灰度策略GrayRule
public class GrayRuleConfig { @Bean public GrayRule grayRule(){ return new GrayRule(); } }
這個GrayRuleConfig不能被掃描進入IOC容器,一旦掃描進入則全局生效
4.openFeign透傳灰度標記
如何從請求頭中取出灰度標記
可以通過實現(xiàn)RequestInterceptor將原先的請求頭給復制過去
@Component @Slf4j public class FeignRequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { HttpServletRequest httpServletRequest = RequestContextUtils.getRequest(); Map<String, String> headers = getHeaders(httpServletRequest); for (Map.Entry<String, String> entry : headers.entrySet()) { //② 設置請求頭到新的Request中 template.header(entry.getKey(), entry.getValue()); } } /** * 獲取原請求頭 */ private Map<String, String> getHeaders(HttpServletRequest request) { Map<String, String> map = new LinkedHashMap<>(); Enumeration<String> enumeration = request.getHeaderNames(); if (enumeration != null) { while (enumeration.hasMoreElements()) { String key = enumeration.nextElement(); String value = request.getHeader(key); //將灰度標記的請求頭透傳給下個服務 if (StrUtil.equals(GrayConstant.GRAY_HEADER,key)&&Boolean.TRUE.toString().equals(value)){ //① 保存灰度發(fā)布的標記 GrayRequestContextHolder.setGrayTag(true); map.put(key, value); } } } return map; } }
也需要使用@RibbonClients注解去標注文章服務調(diào)用的哪些服務需要灰度發(fā)布。
@RibbonClients(value = { //指定對comments這個服務開啟灰度部署 @RibbonClient(value = "comments",configuration = GrayRuleConfig.class) }) public class ArticleApplication {}
5.Nacos中服務如何做灰度標記
配置文件:
spring: cloud: nacos: discovery: metadata: ## 灰度標記 grayTag: true
在Nacos中動態(tài)的指定灰度標記
配置完成之后,在客戶端請求的時候只需要攜帶grayTag=true這個請求頭即可調(diào)用灰度服務。
6.總結(jié)
- 網(wǎng)關中通過全局過濾器實現(xiàn)灰度打標,將灰度標記放入請求頭中傳遞給下游服務
- 網(wǎng)關通過自定義的負載均衡策略,從注冊中心獲取灰度服務,進行轉(zhuǎn)發(fā)
- 在openFeign調(diào)用時需要從請求頭中獲取灰度標記,放入上下文中
- openFeign調(diào)用同樣是根據(jù)自定義的負載均衡策略從注冊中心獲取灰度服務,進行調(diào)用。
- Nacos進行灰度服務調(diào)用
到此這篇關于SpringCloud的全鏈路灰度發(fā)布方案詳解的文章就介紹到這了,更多相關SpringCloud全鏈路灰度發(fā)布內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
如何使用Spring Security手動驗證用戶的方法示例
這篇文章主要介紹了如何使用Spring Security手動驗證用戶的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-05-05java如何執(zhí)行bat腳本,并監(jiān)控執(zhí)行結(jié)果
這篇文章主要介紹了java如何執(zhí)行bat腳本,并監(jiān)控執(zhí)行結(jié)果問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07JavaWeb Struts文件上傳功能實現(xiàn)詳解
這篇文章主要為大家詳細介紹了JavaWeb Struts文件上傳功能實現(xiàn)過程,思路清晰,供大家參考,感興趣的小伙伴們可以參考一下2016-06-06SpringBoot前后端分離解決跨域問題的3種解決方案總結(jié)
前后端分離大勢所趨,跨域問題更是老生常談,下面這篇文章主要給大家介紹了SpringBoot前后端分離解決跨域問題的3種解決方案,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-05-05