SpringCloud-灰度發(fā)布

by shihang.mai

zuul過濾器+ribbon自定義路由規(guī)則+aop

1. 代碼

表結(jié)構(gòu)

  • id
  • serverId
  • userId
  • meta-version

例子以不同的用戶,訪問不同的服務(wù)舉例

  1. 在eureka-client先設(shè)置meta-map的version

新服務(wù)A

eureka:
  instance:
    metadataMap:
      version: v2

舊服務(wù)A

eureka:
  instance:
    metadataMap:
      version: v1
  1. 在zuul中自定義一個(gè)過濾器
@Component
public class GrayFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.ROUTE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        int userId = Integer.parseInt(request.getHeader("userId"));
        // 這里不寫具體的調(diào)用,根據(jù)userId去db獲取規(guī)則,可能獲取不到,即存儲(chǔ)中并沒對(duì)應(yīng)數(shù)據(jù)
        String versioned="v2";

        if(null!=versioned){
            //請(qǐng)求訪問到新服務(wù)上
            RibbonFilterContextHolder.getCurrentContext().add("version",versioned);
        }
        //代碼還需完善..



        return null;
    }
}

引入jar

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

到此,可以完成網(wǎng)關(guān)到服務(wù)則灰度.還需要做服務(wù)之間的灰度.

  1. 在服務(wù)之間我們通過自定義ribbon規(guī)則實(shí)現(xiàn),在服務(wù)中加入路由Rule
@Component
public class GrayRule extends AbstractLoadBalancerRule {
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    @Override
    public Server choose(Object o) {
        return null;
    }


    public Server choose(ILoadBalancer lb, Object o) {

        //獲取所有的可達(dá)的服務(wù)
        List<Server> reachableServers = lb.getReachableServers();

        //獲取當(dāng)前線程的userId-----見第4步
        Map<String,String> map = RibbonParam.get();
        String userId = map.get("userId");
        //根據(jù)用戶id,到db查找version,不寫查找db代碼
        String version = "v2";
        Server returnServer = null;
        //根據(jù)version路由
        for (int i = 0; i < reachableServers.size(); i++) {
            Server server = reachableServers.get(i);
            //這里找到這個(gè)類,是因?yàn)樽约郝嚨?            DiscoveryEnabledServer des=(DiscoveryEnabledServer)server;
            Map<String, String> metadata = des.getInstanceInfo().getMetadata();
            String eurukaClientVersion = metadata.get("version");
            if(eurukaClientVersion.equals(version)){
                returnServer = server;
                break;
            }
        }

        return null;
    }
}

這里用reachableServers獲取單一個(gè)server時(shí),需要轉(zhuǎn)為DiscoveryEnabledServer才能獲取到自定義的meta-map信息

  1. 請(qǐng)求進(jìn)來服務(wù),然后經(jīng)過ribbon路由,它們是一個(gè)線程的,故用ThreadLocal存儲(chǔ)信息
public class RibbonParam {
    private  static final ThreadLocal tl = new ThreadLocal();

    public static <T> T get(){
        return (T)tl.get();
    }

    public static <T> void set(T t){
        tl.set(t);
    }
  1. 那什么時(shí)候把信息set進(jìn)ThreadLocal呢,當(dāng)然是用aop
@Aspect
@Component
public class RequestAspect {

    @Pointcut("execution(* com.shihangmai.eurekaprovider.*.*(..))")
    private void allMethod(){

    }

    @Before(value = "allMethod()")
    public void before(JoinPoint joinPoint){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String userId = request.getHeader("userId");
        Map<String,String> map = new HashMap<>(2);
        map.put("userId",userId);
        RibbonParam.set(map);
    }

}
  1. 注冊(cè)bean
public class GrayRibbonConfiguration {

    @Bean
    public IRule ribbonRule(){
        return new GrayRule();
    }

}

這個(gè)類并不需要加注解,原因見第7步

  1. 在springboot啟動(dòng)類上加上注解
@RibbonClient(name="eureka-provider",configuration = GrayRibbonConfiguration.class)

這樣做的話,只有請(qǐng)求這個(gè)服務(wù)的時(shí)候利用自定義的路由規(guī)則.

到此,網(wǎng)關(guān)到服務(wù),服務(wù)到服務(wù)間的灰度都做完了


  1. 實(shí)際上,3-7步有一個(gè)框架已經(jīng)做了,直接刪除便是,就是
<dependency>
            <groupId>io.jmnarloch</groupId>
            <artifactId>ribbon-discovery-filter-spring-cloud-starter</artifactId>
            <version>2.1.0</version>
</dependency>
  1. 在啟動(dòng)類上的注解RibbonClient也刪除

  2. 當(dāng)我們引入了這個(gè)starter后,只需要在aop中直接加入邏輯即可

@Aspect
@Component
public class RequestAspect {

    @Pointcut("execution(* com.shihangmai.eurekaprovider.*.*(..))")
    private void allMethod(){

    }

    @Before(value = "allMethod()")
    public void before(JoinPoint joinPoint){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String userId = request.getHeader("userId");
        //根據(jù)userId到db中獲取version
        String dbVersion = "v2";
        if(null!= dbVersion){
            RibbonFilterContextHolder.getCurrentContext().add("version", dbVersion);
        }
        
        
    }

}

2. 解析

灰度發(fā)布簡(jiǎn)圖
  1. 新服務(wù)A、舊服務(wù)A,新服務(wù)B、舊服務(wù)B均需要注冊(cè)meta-map:version信息
  2. 當(dāng)請(qǐng)求過來,我們?cè)趜uul中自定義一個(gè)過濾器,從HttpServlet中獲取到token并解析出userId,并將userId到數(shù)據(jù)庫中查出路由的版本。當(dāng)然這個(gè)數(shù)據(jù)庫可以換為redis。
  3. 這樣,我們網(wǎng)關(guān)就可以根據(jù)userId找到庫中的路由版本,然后因?yàn)榉?wù)都注冊(cè)了meta-map:version,路由到對(duì)應(yīng)的服務(wù)器上
    以上是zuul到服務(wù),灰度
  4. 首先,請(qǐng)求經(jīng)過ribbon再從服務(wù)出去,都是同一個(gè)線程。在zuul調(diào)用服務(wù)后,在服務(wù)側(cè)用aop在調(diào)用方法前進(jìn)行加強(qiáng),從HttpServletRequest獲取userId并放入ThreadLocal
  5. 在服務(wù)側(cè)自定義一個(gè)路由規(guī)則并注入到spring上下文,在路由規(guī)則中,獲取ThreadLocal中的userId,然后也是到db找出對(duì)應(yīng)的version,再循環(huán)遍歷所有可達(dá)的服務(wù)列表的meta-data,找到后返回Server即可
  6. 在服務(wù)啟動(dòng)類中指定請(qǐng)求類和對(duì)應(yīng)的自定義路由規(guī)則即可。
    以上是服務(wù)l到服務(wù),灰度

4-6可直接用框架代替為

  1. 首先,請(qǐng)求經(jīng)過ribbon再從服務(wù)出去,都是同一個(gè)線程。在zuul調(diào)用服務(wù)后,在服務(wù)側(cè)用aop在調(diào)用方法前進(jìn)行加強(qiáng),將從HttpServletRequest獲取userId,然后userId到庫中查找對(duì)應(yīng)的version,直接add入RibbonFilterContextHolder.getCurrentContext()即可
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,586評(píng)論 19 139
  • 灰度方案 目前現(xiàn)狀分析 zuul做網(wǎng)關(guān),統(tǒng)一所有內(nèi)部服務(wù)的入口 目前沒有用 eureka 注冊(cè)中心 【目前公司架...
    jey恒閱讀 3,012評(píng)論 0 4
  • 灰度發(fā)布、藍(lán)綠發(fā)布、金絲雀發(fā)布各是什么意思,可以看這篇http://www.appadhoc.com/blog/p...
    staconfree閱讀 7,248評(píng)論 0 7
  • 推薦指數(shù): 6.0 書籍主旨關(guān)鍵詞:特權(quán)、焦點(diǎn)、注意力、語言聯(lián)想、情景聯(lián)想 觀點(diǎn): 1.統(tǒng)計(jì)學(xué)現(xiàn)在叫數(shù)據(jù)分析,社會(huì)...
    Jenaral閱讀 5,984評(píng)論 0 5
  • 昨天,在回家的路上,坐在車?yán)镉圃沼圃盏乜粗摹度龉衬墓适隆罚冶焕锩娴膬?nèi)容深深吸引住了,盡管上學(xué)時(shí)...
    夜闌曉語閱讀 3,943評(píng)論 2 9

友情鏈接更多精彩內(nèi)容