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