<del id="nnjnj"></del><track id="nnjnj"></track>

<p id="nnjnj"></p>

<address id="nnjnj"></address>

    <pre id="nnjnj"><pre id="nnjnj"></pre></pre>

      <noframes id="nnjnj"><ruby id="nnjnj"><ruby id="nnjnj"></ruby></ruby>

      • 自動秒收錄
      • 軟件:1973
      • 資訊:57811|
      • 收錄網站:279872|

      IT精英團

      說說春云的全鏈路灰度發布方案~

      說說春云的全鏈路灰度發布方案~

      瀏覽次數:
      評論次數:
      編輯: 澤洋
      信息來源: ITPUB
      更新日期: 2022-05-05 15:32:23
      摘要

      以下文章來源于公眾號-碼猿技術專欄,作者不才陳某大家好實際生產中如有需求變更,并不會直接更新線上服務,最通常的做法便是:切出線上的小部分流量進行體驗測試,經過測試后無問題則全面的上線。這樣做的好處也是

      • 正文開始
      • 相關閱讀
      • 推薦作品

      以下文章來自微信官方賬號——碼猿科技專欄,作者為陳某。

      大家好!

      在實際生產中,如果需求發生變化,不會直接更新在線服務。最常見的方式是切掉少量在線流量進行體驗測試,測試后沒有問題再完全上線。

      這樣做的好處也非常明顯。一旦出現BUG,可以保證大部分客戶端的正常使用。

      為了實現這種平滑過渡,我們需要使用本文中介紹的全鏈接灰度釋放。

      什么是灰度釋放?

      灰度出版(又稱金絲雀出版)是指一種可以在黑白之間平滑過渡的出版方式??梢詫ζ溥M行A/B測試,即一部分用戶可以繼續使用產品特性A,一部分用戶可以開始使用產品特性B,如果用戶對B沒有異議,那么逐步擴大范圍,將所有用戶遷移到B,灰度釋放可以保證整個系統的穩定性,可以在初始灰度發現問題并進行調整,保證其影響力。

      為什么是全鏈接灰度釋放?

      陳某前面一篇文章介紹了對網關實現灰度發布,只實現了網關路由轉發的灰度發布,如下圖所示:

      如上圖所示,網關的灰度發布實現了網關通過灰度標記路由到文章服務B(灰度服務)。至于文章服務B通過openFeign對評論服務的內部調用,默認無法實現灰度標記的透明傳遞,所以文章服務B最終調用的是評論服務A,而不是評論服務B。

      全鏈接灰度發布需要實現:

      一部分流量通過網關的灰色標記轉發到文章服務B

      服務B可以實現grayTag的透明傳輸,最后調用評論服務B.

      經過以上分析,全鏈路灰度發布需要做到兩點:

      網關轉發實現灰度發布

      該服務通過調用openFeign在內部實現灰度發布(透明灰度標簽)。

      下面以陳某《Spring Cloud Alibaba實戰》欄的服務為例來配置灰度發布。

      網關層的灰色路由轉發

      本文將利用Ribbon Spring云網關對負載均衡策略進行改造,實現灰度發布。

      實現思路如下:

      在網關的全局過濾器中,根據業務規則用灰度標記流量。

      將灰色標記放在請求頭中,并將其傳遞給下游服務。

      重建Ribbon的負載均衡策略,根據流量標記從注冊表獲取灰度服務。

      請求路由轉發

      第一個問題:根據什么條件用灰度標注?

      這就需要根據實際的業務需求,比如用戶所在的地區,使用的客戶端類型,隨機攔截流量。

      在這里,我將直接使用一個標記灰色標簽。只要這個參數在客戶端請求頭中被攜帶并且被設置為true,我將遵循灰度發布邏輯。

      “請求報頭攜帶:grayTag=true。

      "

      第二個問題:為什么要在請求頭中加一個灰色標記,傳遞給下游服務?

      這一步非常關鍵,是實現灰標傳遞給下游服務的關鍵。把灰標放在請求頭中,下游服務只需要從請求頭中獲取灰標就可以知道是不是灰放。這和令牌中繼的原理是一樣的。

      第三個問題:灰度標記如何請求隔離?

      在Spring MVC中,每個請求都是通過打開一個線程來處理的,所以灰色標記可以放在ThreadLocal中用于線程隔離。

      第四個問題:我怎么知道注冊表中的哪個服務是灰度服務?

      Nacos支持在服務中配置一些元數據,可以在元數據中配置灰度標記,以區分灰度服務和正常服務。

      第五個問題:具體服務如何發布灰度?

      例如,my 《Spring Cloud Alibaba實戰》中涉及的一個呼叫鏈接如下:

      tpub.net/JPG/2022-05-05/C55F560702D7391F14AF20F9B56B322A.jpg" width="753" src="https://image.z.itpub.net/zitpub.net/JPG/2022-05-05/C55F560702D7391F14AF20F9B56B322A.jpg">

      需求:現在只對文章服務、評論服務進行灰度發布,其他服務依然使用線上正在運行的服務

      此時的調用關系就變成了下圖:

      我們知道網關路由中配置的服務很多,如何只針對文章服務進行灰度發布呢?

      很簡單:只需要將自定義的Ribbon灰度發布規則只對文章服務生效。

      這里涉及到Ribbon中的一個注解:@RibbonClients ,只需要在其中的value屬性指定需要生效的服務名稱,那么此時網關中的配置如下:

      @RibbonClients(value ={
              //只對文章服務進行灰度發布
              @RibbonClient(value = "article-server",configuration = GrayRuleConfig.class)
      } )
      @SpringBootApplication
      public class GatewayApplication {
         
      }

      @RibbonClient可以指定多個,這個注解有如下兩個屬性:

      • value:指定服務的名稱,在注冊中心配置的服務名稱
      • configuration:自定義的負載均衡策略,這里是灰度發布的策略

      @RibbonClients其中有一個屬性defaultConfiguration,一旦使用這個屬性,那么灰度發布的策略對網關路由中配置的所有服務都將生效。

      第六個問題:說了這么多,具體如何實現?

      網關中首先需要定義一個全局過濾器,偽代碼如下:

      public class GlobalGrayFilter implements GlobalFilter{
          @Override
          public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
               //① 解析請求頭,查看是否存在灰度發布的請求頭信息,如果存在則將其放置在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);
          }
      }

      ①處的代碼:從請求頭中獲取客戶端傳遞過來的灰度標記(這里根據自己業務需要自行更改),判斷是否是灰度發布

      ②處的代碼:GrayRequestContextHolder則是自定義的ThreadLocal實現的線程隔離工具,用來存放灰度標記

      ③處的代碼:將灰度標記放置在請求頭中,傳遞給下游微服務,這里是和令牌一個邏輯。

      注意:這個全局過濾器一定要放在OAuth2.0鑒權過濾器之前,優先級要調高

      全局過濾器中已經將灰度標記打上了,放置在GrayRequestContextHolder中,下面只需要改造Ribbon的負載均衡的策略去注冊中心選擇灰度服務。

      創建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();
                  //灰度發布的服務
                  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);
                      }
                  }
                  //如果被標記為灰度發布,則調用灰度發布的服務
                  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;
              }
          }
      }

      邏輯很簡單,如下:

      1. 獲取灰度標記
      2. 從Nacos注冊中心獲取灰度服務和正常服務
      3. 根據灰度標記去判斷,如果灰度發布則選擇特定的灰度服務進行轉發

      定義一個配置類,注入改造的灰度策略GrayRule,如下:

      /**
       * 灰度部署的負載規則配置類
       * 注意:這個類一定不要被Spring Boot 掃描進入IOC容器中,一旦掃描進入則對全部的服務都將生效
       */
      public class GrayRuleConfig {
          @Bean
          public GrayRule grayRule(){
              return new GrayRule();
          }
      }

      注意:這個GrayRuleConfig不能被掃描進入IOC容器,一旦掃描進入則全局生效

      因為不僅僅網關需要用到這個灰度發布策略,凡是涉及到OpenFeign調用的微服務如果需要配置灰度發布都需要用到,因此這里陳某定義了一個公用的gray-starter。

      經過上述步驟網關的灰度發布則已經配置完成,此時只需要通過@RibbonClients指定對應哪個服務灰度發布。

      openFeign透傳灰度標記

      上面在介紹網關的灰度發布配置時,是將灰度標記(grayTag=true)放在了請求頭中,因此在下游服務中需要做的就只是從請求頭中將灰度標記取出來,然后將其存入GrayRequestContextHolder上下文中。

      這樣一來下游服務中的GrayRule則能從GrayRequestContextHolder獲取到灰度標記,從注冊中心獲取灰度服務進行調用了。

      問題來了:如何從請求頭中取出灰度標記?

      在介紹OAuth2.0相關知識時,曾經出過一篇文章:實戰!openFeign如何實現全鏈路JWT令牌信息不丟失?

      其中介紹了令牌中繼的解決方案,使用的是openFeign的請求攔截器去配置請求頭信息。

      如上圖:openFeign在調用時并不是用的原先的Request,而是內部新建了一個Request,其中復制了請求的URL、請求參數一些信息,但是請求頭并沒有復制過去,因此openFeign調用會丟失請求頭中的信息。

      但是可以通過實現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)){
                          //① 保存灰度發布的標記
                          GrayRequestContextHolder.setGrayTag(true);
                          map.put(key, value);
                      }
                  }
              }
              return map;
          }
      }

      ①處的代碼:從請求頭中獲取灰度發布的標記,設置到GrayRequestContextHolder上下文中

      ②處的代碼:將這個請求頭設置到新的Request中,繼續向下游服務傳遞。

      其實配置一下RequestInterceptor就已經完成了,關于灰度發布策略只需要復用網關的GrayRule

      注意:也需要使用@RibbonClients注解去標注文章服務調用的哪些服務需要灰度發布。

      代碼如下:

      @RibbonClients(value = {
              //指定對comments這個服務開啟灰度部署
              @RibbonClient(value = "comments",configuration = GrayRuleConfig.class)
      })
      public class ArticleApplication {}

      Nacos中服務如何做灰度標記

      其實很簡單,分為兩種:

      1、在配置文件中指定,如下:

      spring:
        cloud:
          nacos:
            discovery:
              metadata:
                ## 灰度標記
                grayTag: true

      2、在Nacos中動態的指定灰度標記

      配置完成之后,在客戶端請求的時候只需要攜帶grayTag=true這個請求頭即可調用灰度服務。

      總結

      微服務中全鏈路灰度發布方案其實很簡單,重要的就是灰度打標,整體流程如下:

      1. 網關中通過全局過濾器實現灰度打標,將灰度標記放入請求頭中傳遞給下游服務
      2. 網關通過自定義的負載均衡策略,從注冊中心獲取灰度服務,進行轉發
      3. 在openFeign調用時需要從請求頭中獲取灰度標記,放入上下文中
      4. openFeign調用同樣是根據自定義的負載均衡策略從注冊中心獲取灰度服務,進行調用。
      標簽:灰度 標記 網關
      誤操作離線丟棄和恢復場景測試的業務影響——來自“血”的教訓
      ? 上一篇 2022-05-05
      JD.COM面試問題:ElasticSearch深度分頁解決方案
      下一篇 ? 2022-05-05
      • 如何在Ubuntu中保留文件系統并備份當前開發板鏡像
        0閱讀 0條評論 個贊
        在Ubuntu保留文件系統或者說備份當前開發板鏡像的需求在不斷增加。比如Ubuntu文件系統需要安裝庫文件的話直接使用apt-get工具就可以下載,但由于需要下載的核心板較多,比較費時間,這時需要將安……
      • 國產核心板全志T507助力消防系統升級
        0閱讀 0條評論 個贊
        9月16日下午,位于湖南長沙市區內的中國電信大樓發生火災,建筑高度218米,現場濃煙滾滾,數十層樓體燃燒劇烈。消防救援人員趕到現場后很快將火勢控制住,目前大樓火勢已被撲滅,所幸未發現人員傷亡。湖南電信……
      • 教大家如何處理Spring Boot易流中的用戶和群體!
        0閱讀 0條評論 個贊
        1.準備工作2.用戶操作2.1添加用戶2.2修改用戶2.3刪除用戶2.4查詢用戶3.組操作3.1添加組3.2修改組3.3刪除組3.4查詢組4.查看表詳情雖然說我們在實際開發中,……
      • 從PG15開始WAL壓縮優化
        0閱讀 0條評論 個贊
        PG15傳聞中的超級令人激動的功能大多數跳票了,年初我也寫過一個關于PG15新功能跳票的文章。PG15BETA已經發出幾個月了,似乎PG15里令人激動人心的功能不多,不過從長長的新功能列表里,……
      • 深入了解美團葉子發射器開源方案
        0閱讀 0條評論 個贊
        大家好,我是樹哥。之前我們有聊過「如何設計一個分布式ID發號器」,其中有講過4種解決方案,分別是:UUID類雪花算法數據庫自增主鍵Redis原子自增美團以第2、3種解決方案為基礎,開發出……
      發表評論 共有條評論
      用戶名: 密碼:
      驗證碼: 匿名發表
      • SQL Server操作系統的任務調度機制
        0閱讀 0條評論 個贊
        簡介SQLServerOS是在Windows之上,用于服務SQLServer的一個用戶級別的操作系統層次。它將操作系統部分的功能從整個SQLServer引擎中抽象出來,單獨形成一層,以便為存……
      • 國產超級大腦———rk 3568核心板!
        13閱讀 0條評論 個贊
        從1956年麥卡錫首次提出“人工智能(AI)”的概念至今,人工智能已有66年的歷史,而人工智能真正的崛起卻是在最近的10年。隨著消費電子行業的快速發展和激烈競爭,人工智能再度流行起來,盡管許多企業對A……
      • 基于iframe的微前端框架——青田
        33閱讀 0條評論 個贊
        vivo互聯網前端團隊-JiangZuohan一、背景VAPD是一款專為團隊協作辦公場景設計的項目管理工具,實踐敏捷開發與持續交付,以「項目」為核心,融合需求、任務、缺陷等應用,使用敏捷迭代、小……
      • 面試官:談談你對mysql事務的認識?
        0閱讀 0條評論 個贊
        引言今天回頭繼續講講數據庫系列的文章。這篇文章屬于mysql數據庫系列,我們來談談事務方面的常見面試題。那么,具體題目有下面這些:1、講講為什么用事務?事務的四大特性?事務的隔離級別知道吧,你們生產……
      • 關于Redis在windows上運行的問題和fork函數
        0閱讀 0條評論 個贊
        Redis在將數據庫進行持久化操作時,需要fork一個進程,但是windows并不支持fork,導致在持久化操作期間,Redis必須阻塞所有的客戶端直至持久化操作完成。微軟的一些工程師花費時間在解決在……
      • 談談動態線程池的9個場景(改進版)
        0閱讀 0條評論 個贊
        大家好,我是小馬哥。線程池是一種基于池化思想管理線程的工具,使用線程池可以減少創建銷毀線程的開銷,避免線程過多導致系統資源耗盡。在高并發以及大批量的任務處理場景,線程池的使用是必不可少的?!?/div>
      • Go語言知識|基本數據類型
        0閱讀 0條評論 個贊
        前言學習Go半年之后,我決定重新開始閱讀《TheGoProgramingLanguage》,對書中涉及重點進行全面講解,這是Go語言知識查漏補缺系列的文章第二篇,前一篇文章則對應書中一二兩章。我……
      • 數據庫發展史II-數據倉庫
        6閱讀 0條評論 個贊
        回顧數據倉庫的發展歷程,大致可以將其分為幾個階段:萌芽探索到全企業集成時代、企業數據集成時代、混亂時代--"數據倉庫之父"間的論戰、理論模型確認時代以及數據倉庫產品百家爭鳴時代。數據倉庫理論發展歷程上……
      • python入門系列(十)學習Python文件處理
        0閱讀 0條評論 個贊
        文件處理在Python中處理文件的關鍵函數是open()函數。有四種不同的方法(模式)來打開一個文件"r"-讀取-默認值。打開一個文件進行讀取,如果文件不存在則出錯。"a"-Append……
      • RDD星火基金簡介(01)
        0閱讀 0條評論 個贊
        1,基本概念RDD(ResilientDistributedDataset):彈性分布式數據集它是Spark中最基本的數據抽象,是編寫Spark程序的基礎。簡單的來講,一個Spark程序可以概括……
      • 百度工程師教你玩設計模式(工廠模式)
        0閱讀 0條評論 個贊
        作者|北極星小組想要寫好代碼,設計模式(DesignPattern)是必不可少的基本功,設計模式是對面向對象設計(ObjectOrientedDesign)中反復出現的問題的解決方案,本篇介紹……
      • 面試問題:Java序列化和反序列化
        0閱讀 0條評論 個贊
        目錄序列化和反序列化的概念應用場景?序列化實現的方式繼承Serializable接口,普通序列化繼承Externalizable接口,強制自定義序列化serialVersionUID的作用靜態變量不會……
      • Java可以重新鎖定的那些東西(1)
        0閱讀 0條評論 個贊
        本文主要包含的內容:可重入鎖(ReedtrantLock)、公平鎖、非公平鎖、可重入性、同步隊列、CAS等概念的理解顯式鎖……
      • python的環境,你再也不用愁康達
        1閱讀 0條評論 個贊
        CondaGuideConda簡介conda是一個包,依賴和環境管理工具,適用于多種語言,如:Python,R,Scala,Java,Javascript,C/C++,FORTRAN……
      • 二戰MySQL數據庫【升華】
        0閱讀 0條評論 個贊
        MYSQL入門系列——第二篇1.篩選條件:(1)比較運算符:(2)邏輯運算符:(3)其他操作:1.排序:2.限制:拓展:3.去重:4.模糊查詢:(like'%')5.范圍查詢:2.聚合與分組(重點……
      • 簡單分析下的GOLDENDB
        0閱讀 0條評論 個贊
        GOLDENDB是金融行業使用的比較廣泛的一個數據庫產品,以前也經常有朋友希望我寫幾篇分析GoldenDB數據庫的文章。說實在的這類文章不好寫,必須對某個產品有一定程度的了解,特別是真正上手用過之后,……
      • 訂單超時案例的RabbitMQ實現
        0閱讀 0條評論 個贊
        前言#人間清醒目錄前言業務場景JUC(DelayQueue)方案DelayQueue簡介JUCDelayQueue實現訂單超時案例代碼案例代碼RedisKey過期事件方案簡介RabbitKey……
      • 漫談考試成長的探索——考試策略
        0閱讀 0條評論 個贊
        在《漫談軟件系統測試——問題解決》一文中,文章借鑒控制疫情的四大策略,總結了軟件系統質量保障的四大策略。那么在日常工作中,我們應該如何理解測試策略呢?什么是測試策略?測試策略是描述軟件開發周期的測試方……
      • 舉例說明庫伯內特公司的豆莢核心資源
        3閱讀 0條評論 個贊
        目錄一、Pod定義二、Pod入門yaml描述文件三、共享NetworkNamespace四、共享PID五、容器生命周期六、初始化容器6.1、簡介6.2、與普通容器的區別6.3、實驗七、Pod探針7.1……
      • SQL Server動態生成分區腳本
        0閱讀 0條評論 個贊
        一、前言前段時間使用表分區比較多,雖然已經寫了SQLServer合并(刪除)分區解惑、SQLServer2005分區模板與實例,但是在實踐中一直感覺修改SQL腳本的時間比較多,一直想抽個時間來把……
      最近發布資訊
      更多
      警花高潮嗷嗷叫
      <del id="nnjnj"></del><track id="nnjnj"></track>

      <p id="nnjnj"></p>

      <address id="nnjnj"></address>

        <pre id="nnjnj"><pre id="nnjnj"></pre></pre>

          <noframes id="nnjnj"><ruby id="nnjnj"><ruby id="nnjnj"></ruby></ruby>