SpringCloud實(shí)現(xiàn)灰度發(fā)布的方法步驟
1.什么是灰度發(fā)布?
灰度發(fā)布又稱金絲雀發(fā)布,是在系統(tǒng)升級(jí)的時(shí)候能夠平滑過(guò)渡的一種發(fā)布方式。在其上可以進(jìn)行A/B測(cè)試,即讓一部分用戶繼續(xù)用產(chǎn)品特性A,一部分用戶開(kāi)始用產(chǎn)品特性B,如果用戶對(duì)B沒(méi)有什么反對(duì)意見(jiàn),那么逐步擴(kuò)大范圍,把所有用戶都遷移到B上面來(lái)?;叶劝l(fā)布可以保證整體系統(tǒng)的穩(wěn)定,在初始灰度的時(shí)候就可以發(fā)現(xiàn)、調(diào)整問(wèn)題,以保證其影響度。
關(guān)于金絲雀發(fā)布名稱的來(lái)歷:礦工下要礦井,要驗(yàn)證是否有瓦斯,金絲雀對(duì)瓦斯很敏感,通過(guò)觀察金絲雀的反應(yīng)判斷是否安全。
2.灰度發(fā)布有什么作用?
1.降低發(fā)布帶來(lái)的影響,雖然功能都在測(cè)試環(huán)境測(cè)過(guò),但畢竟沒(méi)有發(fā)布到生產(chǎn)環(huán)境,如果先讓少部分用戶先使用新版本,提前發(fā)現(xiàn)bug,或者性能問(wèn)題,提前做好修復(fù),就可以降低新版本帶來(lái)的影響;
2.通過(guò)對(duì)新老版本的對(duì)比,觀察新版本帶來(lái)的效果。結(jié)合工作中使用到的灰度發(fā)布實(shí)踐和對(duì)其他大廠的灰度發(fā)布調(diào)研,總結(jié)了以下灰度發(fā)布方案。
3.灰度發(fā)布的實(shí)現(xiàn)方式:網(wǎng)關(guān)到服務(wù),服務(wù)到服務(wù)
3.1網(wǎng)關(guān)到服務(wù)代碼實(shí)現(xiàn)
3.1.1整體流程
指定灰度規(guī)則->預(yù)制代碼規(guī)則->springcloud自定義metadata
3.1.2前置環(huán)境(需要自行搭建四個(gè)至少服務(wù))
- eureka:注冊(cè)中心
- zuul:網(wǎng)關(guān)
- service-v1:集群服務(wù)v1版本
- service-v2:集群服務(wù)v2版本
3.1.3核心代碼
pom.xml
<!-- 實(shí)現(xiàn)通過(guò) metadata 進(jìn)行灰度路由 -->
<dependency>
<groupId>io.jmnarloch</groupId>
<artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
<version>2.1.0</version>
</dependency>
灰度過(guò)濾器(核心代碼)
@Component
public class GrayFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;//return false 關(guān)閉該過(guò)濾器
}
@Autowired
private CommonGrayRuleDaoCustom commonGrayRuleDaoCustom;
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
String userId = request.getHeader("userId");
// 根據(jù)用戶id查規(guī)則查庫(kù),
String rule = findRuleById(userId);
// 金絲雀
if ("v1".equals(rule)) {
RibbonFilterContextHolder.getCurrentContext().add("version", "v1");
// 普通用戶
} else if ("v2".equals(rule)) {
RibbonFilterContextHolder.getCurrentContext().add("version", "v2");
}
return null;
}
//查庫(kù)的偽代碼
private String findRuleById(String userId) {
Map<String, String> map = new HashMap();
map.put("9527", "v1");
map.put("9528", "v2");
return map.get(userId);
}
}
3.2網(wǎng)關(guān)到服務(wù)代碼實(shí)現(xiàn)
3.2.1整體流程
springcloud自定義metadata->獲取當(dāng)前用戶的版本->遍歷服務(wù)獲取服務(wù)的的版本,返回合適的服務(wù)
3.2.2前置環(huán)境(需要自行搭建5個(gè)至少服務(wù))
- eureka:注冊(cè)中心
- service-A:服務(wù)調(diào)用方
- service-v1:集群服務(wù)v1版本
- service-v2:集群服務(wù)v2版本
3.2.3核心代碼
threadlocal工具類
public class RibbonParameters {
private static final ThreadLocal local = new ThreadLocal();
public static <T> T get() {
return (T) local.get();
}
public static <T> void set(T t) {
local.set(t);
}
}
切面獲取version的值
@Aspect
@Component
public class RequestAspect {
@Pointcut("execution(* com.mashibing.apipassenger.controller..*Controller*.*(..))")
private void anyMehtod() {
}
@Before(value = "anyMehtod()")
public void before(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String version = request.getHeader("version");
//方式二:
HashMap<Object, Object> map = new HashMap<>();
map.put("version",version);
RibbonParameters.set(map);
}
rule規(guī)則
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Map;
@Configuration
public class GrayRule extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
private Server choose(ILoadBalancer lb, Object key) {
System.out.println("灰度,rule");
Server server = null;
while (server == null) {
List<Server> reachableServers = lb.getReachableServers();
//獲取當(dāng)前線程的參數(shù) 用戶 version=v1
Map<String, String> map = (Map<String, String>) RibbonParameters.get();
String version = "";
if (map != null && map.containsKey("version")) {
version = map.get("version");
}
System.out.println("當(dāng)前rule,version=" + version);
//遍歷服務(wù)列表選取用戶服務(wù)
for (int i = 0; i < reachableServers.size(); i++) {
server = reachableServers.get(i);
//用戶的version知道了,服務(wù)自定義的meta不知道
Map<String, String> metadata = ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata();
String metaMap = metadata.get("version");
//用戶的version知道了,服務(wù)meta也知道了
if (version.trim().equals(metaMap)) {
return server;
}
}
}
return null;
}
}
注意:提前踩坑,No qualifying bean of type ‘com.netflix.loadbalancer.IRule‘ available: expected single matching bean

當(dāng)是覺(jué)得很奇怪,命名自己只定義了grayRule負(fù)載均衡策略規(guī)則,metadataAwareRule這個(gè)我代碼中并沒(méi)有。經(jīng)過(guò)排查自己使用在pom中引入了Ribbon的包,該包默認(rèn)會(huì)帶負(fù)載均衡策略規(guī)則。導(dǎo)致有多個(gè)規(guī)則,從而報(bào)錯(cuò)。
<dependency>
<groupId>io.jmnarloch</groupId>
<artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
<version>2.1.0</version>
</dependency>刪除該包即可
刪除后重新運(yùn)行

服務(wù)與服務(wù)的灰度發(fā)布的另外一種方式:可以在requestAspect中獲取到version后,直接比對(duì)版本:RibbonFilterContextHolder.getCurrentContext().add("version", "v1"),這種凡是與網(wǎng)關(guān)與服務(wù)的灰度發(fā)布相似。
自此灰度發(fā)布完成。
到此這篇關(guān)于SpringCloud實(shí)現(xiàn)灰度發(fā)布的方法步驟的文章就介紹到這了,更多相關(guān)SpringCloud 灰度發(fā)布內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring Cloud 優(yōu)雅下線以及灰度發(fā)布實(shí)現(xiàn)
- springcloud+nacos實(shí)現(xiàn)灰度發(fā)布示例詳解
- 關(guān)于SpringCloud灰度發(fā)布的實(shí)現(xiàn)
- SpringCloud灰度發(fā)布的設(shè)計(jì)與實(shí)現(xiàn)詳解
- SpringCloud的全鏈路灰度發(fā)布方案詳解
- Spring?Cloud實(shí)現(xiàn)灰度發(fā)布的示例代碼
- SpringCloud實(shí)現(xiàn)全鏈路灰度發(fā)布的示例詳解
- Spring Cloud Gateway實(shí)現(xiàn)灰度發(fā)布方案
相關(guān)文章
Java數(shù)據(jù)結(jié)構(gòu)之鏈表詳解
本篇文章我們將講解一種新型的數(shù)據(jù)結(jié)構(gòu)—鏈表,鏈表是一種使用廣泛的通用數(shù)據(jù)結(jié)構(gòu),它可以用來(lái)作為實(shí)現(xiàn)棧,隊(duì)列等數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ).文中有非常詳細(xì)的介紹,需要的朋友可以參考下2021-05-05
mybatisplus中的xml對(duì)象參數(shù)傳遞問(wèn)題
這篇文章主要介紹了mybatisplus中的xml對(duì)象參數(shù)傳遞問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
SpringBoot項(xiàng)目的兩種發(fā)布方式
本文主要介紹了SpringBoot項(xiàng)目的兩種發(fā)布方式,包含jar包發(fā)布和war包發(fā)布,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07
Java面試題沖刺第四天--數(shù)據(jù)庫(kù)
這篇文章主要為大家分享了最有價(jià)值的三道數(shù)據(jù)庫(kù)面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下2021-07-07
java中char對(duì)應(yīng)的ASCII碼的轉(zhuǎn)化操作
這篇文章主要介紹了java中char對(duì)應(yīng)的ASCII碼的轉(zhuǎn)化操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08
SpringBoot中統(tǒng)計(jì)方法耗時(shí)的七種實(shí)現(xiàn)方式小結(jié)
作為開(kāi)發(fā)者,我們經(jīng)常需要統(tǒng)計(jì)方法的執(zhí)行時(shí)間,以便找出性能瓶頸,優(yōu)化系統(tǒng)響應(yīng)速度,今天分享在SpringBoot框架中實(shí)現(xiàn)方法耗時(shí)統(tǒng)計(jì)的幾種方法,大家可以根據(jù)需求自行選擇2025-03-03
springboot普通類中如何獲取session問(wèn)題
這篇文章主要介紹了springboot普通類中如何獲取session問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01

