SpringCloud實現(xiàn)灰度發(fā)布的方法步驟
1.什么是灰度發(fā)布?
灰度發(fā)布又稱金絲雀發(fā)布,是在系統(tǒng)升級的時候能夠平滑過渡的一種發(fā)布方式。在其上可以進行A/B測試,即讓一部分用戶繼續(xù)用產品特性A,一部分用戶開始用產品特性B,如果用戶對B沒有什么反對意見,那么逐步擴大范圍,把所有用戶都遷移到B上面來?;叶劝l(fā)布可以保證整體系統(tǒng)的穩(wěn)定,在初始灰度的時候就可以發(fā)現(xiàn)、調整問題,以保證其影響度。
關于金絲雀發(fā)布名稱的來歷:礦工下要礦井,要驗證是否有瓦斯,金絲雀對瓦斯很敏感,通過觀察金絲雀的反應判斷是否安全。
2.灰度發(fā)布有什么作用?
1.降低發(fā)布帶來的影響,雖然功能都在測試環(huán)境測過,但畢竟沒有發(fā)布到生產環(huán)境,如果先讓少部分用戶先使用新版本,提前發(fā)現(xiàn)bug,或者性能問題,提前做好修復,就可以降低新版本帶來的影響;
2.通過對新老版本的對比,觀察新版本帶來的效果。結合工作中使用到的灰度發(fā)布實踐和對其他大廠的灰度發(fā)布調研,總結了以下灰度發(fā)布方案。
3.灰度發(fā)布的實現(xiàn)方式:網(wǎng)關到服務,服務到服務
3.1網(wǎng)關到服務代碼實現(xiàn)
3.1.1整體流程
指定灰度規(guī)則->預制代碼規(guī)則->springcloud自定義metadata
3.1.2前置環(huán)境(需要自行搭建四個至少服務)
- eureka:注冊中心
- zuul:網(wǎng)關
- service-v1:集群服務v1版本
- service-v2:集群服務v2版本
3.1.3核心代碼
pom.xml
<!-- 實現(xiàn)通過 metadata 進行灰度路由 --> <dependency> <groupId>io.jmnarloch</groupId> <artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId> <version>2.1.0</version> </dependency>
灰度過濾器(核心代碼)
@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 關閉該過濾器 } @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ī)則查庫, 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; } //查庫的偽代碼 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)關到服務代碼實現(xiàn)
3.2.1整體流程
springcloud自定義metadata->獲取當前用戶的版本->遍歷服務獲取服務的的版本,返回合適的服務
3.2.2前置環(huán)境(需要自行搭建5個至少服務)
- eureka:注冊中心
- service-A:服務調用方
- service-v1:集群服務v1版本
- service-v2:集群服務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(); //獲取當前線程的參數(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("當前rule,version=" + version); //遍歷服務列表選取用戶服務 for (int i = 0; i < reachableServers.size(); i++) { server = reachableServers.get(i); //用戶的version知道了,服務自定義的meta不知道 Map<String, String> metadata = ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata(); String metaMap = metadata.get("version"); //用戶的version知道了,服務meta也知道了 if (version.trim().equals(metaMap)) { return server; } } } return null; } }
注意:提前踩坑,No qualifying bean of type ‘com.netflix.loadbalancer.IRule‘ available: expected single matching bean
當是覺得很奇怪,命名自己只定義了grayRule負載均衡策略規(guī)則,metadataAwareRule這個我代碼中并沒有。經過排查自己使用在pom中引入了Ribbon的包,該包默認會帶負載均衡策略規(guī)則。導致有多個規(guī)則,從而報錯。
<dependency> <groupId>io.jmnarloch</groupId> <artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId> <version>2.1.0</version> </dependency>
刪除該包即可
刪除后重新運行
服務與服務的灰度發(fā)布的另外一種方式:可以在requestAspect中獲取到version后,直接比對版本:RibbonFilterContextHolder.getCurrentContext().add("version", "v1"),這種凡是與網(wǎng)關與服務的灰度發(fā)布相似。
自此灰度發(fā)布完成。
到此這篇關于SpringCloud實現(xiàn)灰度發(fā)布的方法步驟的文章就介紹到這了,更多相關SpringCloud 灰度發(fā)布內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Springboot Autowried及Resouce使用對比解析
這篇文章主要介紹了Springboot Autowried及Resouce使用對比解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-06-06詳解Spring框架下向異步線程傳遞HttpServletRequest參數(shù)的坑
這篇文章主要介紹了詳解Spring框架下向異步線程傳遞HttpServletRequest參數(shù)的坑,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-03-03SpringBoot在一定時間內限制接口請求次數(shù)的實現(xiàn)示例
在項目中,接口的暴露在外面,很多人就會惡意多次快速請求,本文主要介紹了SpringBoot在一定時間內限制接口請求次數(shù)的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下2022-03-03