国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

鏈路追蹤在開源SpringBoot/SpringCloud微服務框架的實踐

這篇具有很好參考價值的文章主要介紹了鏈路追蹤在開源SpringBoot/SpringCloud微服務框架的實踐。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

鏈路追蹤在開源SpringBoot/SpringCloud微服務框架的實踐

前期內容導讀:

  1. Java開源RSA/AES/SHA1/PGP/SM2/SM3/SM4加密算法介紹
  2. Java開源AES/SM4/3DES對稱加密算法介紹及其實現(xiàn)
  3. Java開源AES/SM4/3DES對稱加密算法的驗證說明
  4. Java開源RSA/SM2非對稱加密算法對比介紹
  5. Java開源RSA非對稱加密算法實現(xiàn)
  6. Java開源SM2非對稱加密算法實現(xiàn)
  7. Java開源接口微服務代碼框架
  8. Json在開源SpringBoot/SpringCloud微服務框架中的最佳實踐
  9. 加解密在開源SpringBoot/SpringCloud微服務框架的最佳實踐
  • 在前面詳細介紹的基礎上,且代碼全部開源后,這次來完整介紹下鏈路追蹤在SpringBoot/SpringCloud微服務中到底是如何應用的。
  • 應該是先有業(yè)務,才會有微服務設計。此開源的微服務設計見Java開源接口微服務代碼框架 文章,現(xiàn)把核心設計摘錄如下:

1. 開源代碼整體設計

                                                     +------------+
                                                     |   bq-log   |
                                                     |            |
                                                     +------------+
                                                    Based on SpringBoot
                                                            |
                                                            |
                                                            v
     +------------+           +------------+         +------------+         +-------------------+
     |bq-encryptor|  +----->  |   bq-base  | +-----> |bq-boot-root| +-----> | bq-service-gateway|
     |            |           |            |         |            |         |                   |
     +------------+           +------------+         +------------+         +-------------------+
  Based on BouncyCastle      Based on Spring       Based on SpringBoot    Based on SpringBoot-WebFlux
                                                            +
                                                            |
                                                            v
                                                     +------------+         +-------------------+
                                                     |bq-boot-base| +-----> | bq-service-auth   |
                                                     |            |     |   |                   |
                                                     +------------+     |   +-------------------+
                                                 ased on SpringBoot-Web | Based on SpringSecurity-Authorization-Server
                                                                        |
                                                                        |
                                                                        |
                                                                        |   +-------------------+
                                                                        +-> | bq-service-biz    |
                                                                            |                   |
                                                                            +-------------------+

說明:

  1. bq-encryptor:基于BouncyCastle安全框架,已開源 ,加解密介紹
    ,支持RSA/AES/PGP/SM2/SM3/SM4/SHA-1/HMAC-SHA256/SHA-256/SHA-512/MD5等常用加解密算法,并封裝好了多種使用場景、做好了為SpringBoot所用的準備;
  2. bq-base:基于Spring框架的基礎代碼框架,已開源 ,支持json/redis/DataSource/guava/http/tcp/thread/jasypt等常用工具API;
  3. bq-log:基于SpringBoot框架的基礎日志代碼,已開源 ,支持接口Access日志、調用日志、業(yè)務操作日志等日志文件持久化,可根據(jù)實際情況擴展;
  4. bq-boot-root:基于SpringBoot,已開源 ,但是不包含spring-boot-starter-web,也不包含spring-boot-starter-webflux,可通用于servletnettyweb容器場景,封裝了redis/http /定時器/加密機/安全管理器等的自動注入;
  5. bq-boot-base:基于spring-boot-starter-web(servlet,BIO),已開源 ,提供常規(guī)的業(yè)務服務基礎能力,支持PostgreSQL/限流/bq-log/Web框架/業(yè)務數(shù)據(jù)加密機加密等可配置自動注入;
  6. bq-service-gateway:基于spring-boot-starter-webflux(Netty,NIO),已開源 ,提供了Jwt Token安全校驗能力,包括接口完整性校驗/接口數(shù)據(jù)加密/Jwt Token合法性校驗等;
  7. bq-service-auth:基于spring-security-oauth2-authorization-server,已開源 ,提供了JwtToken生成和刷新的能力;
  8. bq-service-biz:業(yè)務微服務參考樣例,已開源 ;

2. 微服務邏輯架構設計

                           +-------------------+
                           |  Web/App Client   |
                           |                   |
                           +-------------------+
                                     |
                                     |
                                     v
  +--------------------------------------------------------------------+
  |                 |         Based On K8S                             |
  |                 |1                                                 |
  |                 v                                                  |
  |       +-------------------+    2      +-------------------+        |
  |       | bq-service-gateway| +-------> | bq-service-auth   |        |
  |       |                   |           |                   |        |
  |       +-------------------+           +-------------------+        |
  |                 |3                                                 |
  |                 +-------------------------------+                  |
  |                 v                               v                  |
  |       +-------------------+           +-------------------+        |
  |       | bq-service-biz1   |           | bq-service-biz2   |        |
  |       |                   |           |                   |        |
  |       +-------------------+           +-------------------+        |
  |                                                                    |
  +--------------------------------------------------------------------+

說明:

  1. bq-service-gateway:基于SpringCloud-Gateway,用作JwtToken鑒權,并提供了接口、數(shù)據(jù)加解密的安全保障能力;
  2. bq-service-auth:基于spring-security-oauth2-authorization-server,提供了JwtToken生成和刷新的能力;
  3. bq-service-biz:基于spring-boot-starter-web,業(yè)務微服務參考樣例;
  4. k8s在上述微服務架構中,承擔起了服務注冊和服務發(fā)現(xiàn)的作用,鑒于k8s云原生環(huán)境構造較為復雜,實際開源的代碼時,以Nacos(為主)/Eureka做服務注冊和服務發(fā)現(xiàn)中間件;
  5. 以上所有服務都以docker容器作為載體,確保服務有較好地集群遷移和彈性能力,并能夠逐步平滑遷移至k8s的終極目標;
  6. 邏輯架構不等同于物理架構(部署架構),實際業(yè)務部署時,還有DMZ區(qū)和內網區(qū),本邏輯架構做了簡化處理;

3. 鏈路追蹤框架選型

3.1 為什么要引入鏈路追蹤

  • 隨著分布式微服務的發(fā)展,服務在小型化的同時,服務數(shù)據(jù)急劇膨脹,導致調用鏈條特別復雜特別長,定位問題和數(shù)據(jù)提取比較困難;
  • 微服務化也促使用平價的服務器(一般是VM,或者叫ECS)來替代價格高昂的專用服務器,所以會導致服務的穩(wěn)定性變差,所以也需要關注資源的性能瓶頸;
  • 鏈路追蹤并非必須的,在傳統(tǒng)項目、服務數(shù)量稀少、業(yè)務相對簡單的項目就沒有必要使用,在云原生微服務架構中則很有必要引入;

3.2 鏈路追蹤能做什么

  • 鏈路追蹤是為了解決技術痛點的,其核心價值在于:評估并記錄服務間的調用鏈數(shù)據(jù);我們可以基于這些數(shù)據(jù)清晰地知道客戶請求的來龍去脈,系統(tǒng)出現(xiàn)問題的大致位置。
  • 鏈路追蹤不關心服務內部觸發(fā)的其它調用鏈,比如:服務內的定時器、服務內的初始化服務等;

3.3 當下鏈路追蹤框架對比

  • 鏈路追蹤技術基本上都是Google Dapper,當下有2種不同的實現(xiàn):

    • 代碼侵入式的引用,如:zipkin/cat;
    • 代碼無侵入式的引用,如:SkyWalking/Pinpoint;

    二者的區(qū)別:前者需要通過把鏈路追蹤的Java包當做依賴加入到依賴庫中;后者則是在執(zhí)行啟動命令時,帶上鏈路追蹤的jar包即可,鏈路監(jiān)控完全基于字節(jié)碼增強技術來實現(xiàn);

  • 當下較多使用的鏈路追蹤框架如下表所示:

    鏈路追蹤特性 Cat Zipkin SkyWalking Pinpoint
    調用鏈可視化
    聚合報表 非常豐富 較豐富 非常豐富
    服務依賴圖 簡單 簡單
    埋點方式 侵入式 侵入式 非侵入式,字節(jié)碼增強 非侵入式,字節(jié)碼增強
    VM監(jiān)控指標
    支持語言 java/.net 豐富 java/.net/php/go/node.js java/php/python
    存儲機制 mysql(報表),本地文件/HDFS(調用鏈) 內存/redis/es/mysql等 H2、es HBase
    社區(qū)支持 主要在國內 國外主流 Apache支持 -
    使用案例 美團、攜程 京東、阿里定制后不開源 華為、小米 -
    APM
    開發(fā)基礎 eBay cal Google Dapper Google Dapper Google Dapper
    是否支持WebFlux

    結合實際情況:

    • 我們有SpringCloud-Gateway(基于WebFlux),所以不能使用Cat/Pinpoint
    • 我們當下只要加上鏈路追蹤即可,再加上zipkin是SpringCloud的親兒子,對應的SpringCloud組件為SpringCloud-Sleuth,所以此框架優(yōu)先選用了zipkin,暫沒有必要去使用牛刀SkyWalking;
  • 綜上,我們選擇小巧而且與SpringCloud框架最密切的zipkin作為我們的鏈路追蹤框架,缺點就是它是代碼侵入式的,它的變更可能會影響業(yè)務穩(wěn)定。

3.4 在項目中引入zipkin

  • 引入maven依賴 :
    <!--for trace id-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-sleuth-zipkin</artifactId>
        <version>3.1.7</version>
    </dependency>
    <!--for monitor trace-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-sleuth</artifactId>
        <version>3.1.7</version>
    </dependency>
    

    工程引入sleuth和zipkin時,可能會存在jar包依賴沖突,尤其是要兼顧webflux時,有興趣可以看看工程中的真實引用關系。maven沖突的問題就不單獨講了。

  • 下載zipkin 源碼,按照指導文檔進行編譯(不要最新版本,要找和SpringCloud匹配的版本)。編譯成功后,進入zipkin-server目錄,執(zhí)行zipkin啟動命令:
    java -jar ./zipkin-server/target/zipkin-server-*exec.jar
    
  • 在spring yaml 配置文件中配置zipkin信息:
    spring:
      sleuth:
        sampler:
          #采樣率值介于0到1之間,1則表示全部采集
          probability: 1
      zipkin:
        #Zipkin的訪問地址
        base-url: http://localhost:9411
    
  • 為了讓日志格式在Spring中定制,logback日志配置文件最好是使用logback-spring.xml,不要使用logback.xml;
  • 日志分為2種,一種是Access日志,另一種是運行日志。我們現(xiàn)在就要保證2種日志都有鏈路ID;
  • SpringBoot的yaml配置 為:
    logging:
      name: ${spring.application.name}
      config: classpath:logback-spring.xml
      basedir: /***/logs/${spring.application.name}/
      format: "%d{yy-MM-dd HH:mm:ss.SSS}[${spring.application.name}][Tid:%X{traceId:-},Sid:%X{spanId:-}][%level][%logger{20}_%M] - %msg%n"
    

4. SpringBoot服務引入zipkin

  • 對應的logback-spring.xml :
    <?xml version="1.0" encoding="UTF-8"?>
    <!--日志級別以及優(yōu)先級排序: FATAL > ERROR > WARN > INFO > DEBUG-->
    <configuration debug="false">
        <springProperty scope="context" name="LOG_SERVICE" source="spring.application.name" defaultValue="bq-service"/>
        <springProperty scope="context" name="INSTANCE_ID" source="server.port" defaultValue="8080"/>
        <springProperty scope="context" name="BASE_LOG_PATH" source="logging.basedir" defaultValue="/temp/${LOG_SERVICE}"/>
        <!-- 日志默認輸出級別 -->
        <springProperty scope="context" name="LOG_LEVEL" source="log.level.ROOT" defaultValue="INFO"/>
        <!-- 日志文件默認輸出格式,不帶行號輸出(行號顯示會影響日志輸出性能);%C:大寫,類名;%M:方法名;%m:錯誤信息;%n:換行 -->
        <!--%d{yy-MM-dd HH:mm:ss.SSS}[TxId:%X{PtxId},SpanId:%X{PspanId}][${LOG_SERVICE}][%level][%logger{20}_%M] - %msg%n-->
        <springProperty scope="context" name="LOG_PATTERN" source="logging.format" defaultValue="%msg%n"/>
        <!-- 日志默認切割的最小單位 -->
        <springProperty scope="context" name="MAX_FILE_SIZE" source="logging.file-size" defaultValue="100MB"/>
        <!--單機直接運行時這樣區(qū)分-->
        <property name="LOG_PATH" value="${BASE_LOG_PATH}/${LOG_SERVICE}_${INSTANCE_ID}"/>
    
        <!--使用自定義的access日志 -->
        <appender name="accessAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_PATH}/access.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <FileNamePattern>${LOG_PATH}/%d{yy-MM-dd}/access-%d{yy-MM-dd}.log</FileNamePattern>
                <maxHistory>30</maxHistory>
            </rollingPolicy>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${AUDIT_LOG_PATTERN}</pattern>
                <charset>UTF-8</charset>
            </encoder>
        </appender>
    
        <!--控制臺日志-->
        <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${LOG_PATTERN}</pattern>
                <charset>UTF-8</charset>
            </encoder>
        </appender>
    
        <!--default日志 -->
        <appender name="defaultAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_PATH}/default.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <FileNamePattern>${LOG_PATH}/%d{yy-MM-dd}/default-%d{yy-MM-dd}.log</FileNamePattern>
                <maxHistory>30</maxHistory>
            </rollingPolicy>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${LOG_PATTERN}</pattern>
                <charset>UTF-8</charset>
            </encoder>
        </appender>
        <!--error日志 -->
        <appender name="errorAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_PATH}/error.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <FileNamePattern>${LOG_PATH}/%d{yy-MM-dd}/error-%d{yy-MM-dd}.log</FileNamePattern>
                <maxHistory>30</maxHistory>
            </rollingPolicy>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${LOG_PATTERN}</pattern>
                <charset>UTF-8</charset>
            </encoder>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <level>ERROR</level>
                <onMatch>ACCEPT</onMatch>
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>
        <!--默認日志-->
        <logger name="com.biuqu" additivity="false">
            <appender-ref ref="consoleAppender"/>
            <appender-ref ref="defaultAppender"/>
        </logger>
    
        <!--access日志-->
        <logger name="com.biuqu.boot.model.MdcAccessLogValve" additivity="false">
            <appender-ref ref="accessAppender"/>
        </logger>
    
        <!--全局異常日志-->
        <logger name="com.biuqu.boot.handler.GlobalExceptionHandler" additivity="false">
            <appender-ref ref="errorAppender"/>
            <appender-ref ref="defaultAppender"/>
        </logger>
        <!--建立一個默認的root的logger -->
        <root level="${LOG_LEVEL}">
            <appender-ref ref="consoleAppender"/>
            <appender-ref ref="defaultAppender"/>
        </root>
    </configuration>
    

    仔細觀察就可以看出logging.format是從SpringBoot yaml配置中傳入logback的,是Access Log/運行日志/錯誤日志/Console日志的格式字段,帶有traceIdSpanId字段;

  • 深入研究就會發(fā)現(xiàn)常規(guī)SpringBoot微服務(基于tomcat),access log并沒有鏈路信息,還需要對框架進一步改造。
    • 需要先定制Tomcat的Access Log工廠類,因此新增一個日志的配置服務LogConfigurer ,代碼如下:
      @Configuration
      public class LogConfigurer
      {
          /**
           * 在tomcat日志中實現(xiàn)trace id
           * <p>
           * 參考: https://www.appsloveworld.com/springboot/100/36/mdc-related-content-in-tomcat-access-logs
           *
           * @param env 運行環(huán)境變量
           * @return 定制的AccessLog工廠
           */
          @Bean
          public WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory> accessLog(Environment env)
          {
              return factory ->
              {
                  final AccessLogValve valve = new MdcAccessLogValve();
                  valve.setPattern(env.getProperty("server.tomcat.accesslog.pattern"));
      
                  //直接覆蓋原生的日志對象
                  if (factory instanceof TomcatServletWebServerFactory)
                  {
                      TomcatServletWebServerFactory tsFactory = (TomcatServletWebServerFactory)factory;
                      tsFactory.setEngineValves(Lists.newArrayList(valve));
                  }
              };
          }
      }
      
    • 在LogConfigurer 中自定義了一個MdcAccessLogValve 對象,代碼如下:
      @Slf4j
      public class MdcAccessLogValve extends AccessLogValve
      {
          @Override
          public void log(CharArrayWriter message)
          {
              log.info(message.toString());
          }
      
          @Override
          protected AccessLogElement createAccessLogElement(String name, char pattern)
          {
              if (pattern == CommonBootConst.TRACE_TAG)
              {
                  return (buf, date, request, response, time) ->
                  {
                      //兼容沒有sleuth時的場景
                      boolean existTrace = ClassUtils.isPresent(SLEUTH_TYPE, this.getClass().getClassLoader());
                      if (!existTrace)
                      {
                          buf.append(Const.MID_LINK);
                          return;
                      }
      
                      Object context = request.getRequest().getAttribute(TraceContext.class.getName());
                      if (!(context instanceof TraceContext))
                      {
                          return;
                      }
                      TraceContext traceContext = (TraceContext)context;
                      if (CommonBootConst.TRACE_ID.equalsIgnoreCase(name))
                      {
                          buf.append(traceContext.traceId());
                      }
                      else if (CommonBootConst.SPAN_ID.equalsIgnoreCase(name))
                      {
                          buf.append(traceContext.spanId());
                      }
                  };
              }
              return super.createAccessLogElement(name, pattern);
          }
      
          /**
           * Sleuth存在的key
           */
          private static final String SLEUTH_TYPE = "org.springframework.cloud.sleuth.TraceContext";
      }
      
      • MdcAccessLogValve設計時,兼容了使用sleuth和不使用sleuth2種情況。
      • AccessLog打印出來后,就會發(fā)現(xiàn)會多了一些健康檢查日志,注意不要把心跳檢查設置得過于頻繁;
  • 至此,基于SpringBoot常規(guī)的微服務大部分情況下有鏈路ID了。但是由于定制了線程池和異步任務池,存在如下2種異常情況:
    • 在接收到請求后,用線程池起多線程執(zhí)行任務,在多線程日志里面沒有鏈路ID;
    • 在接收到請求后,起了異步任務,在異步任務日志里面沒有鏈路ID;
  • 由于sleuth底層還是用了MDC來做線程間的日志數(shù)據(jù)隔離,解決辦法是繼續(xù)在自定義的線程工廠CommonThreadFactory 時,從主線程中設置到子線程中去:
    public class CommonThreadFactory implements ThreadFactory
    {
        @Override
        public Thread newThread(Runnable r)
        {
            //獲取主線程的鏈路信息
            MDCAdapter mdc = MDC.getMDCAdapter();
            Map<String, String> map = mdc.getCopyOfContextMap();
            Thread t = new Thread(r, this.poolPrefix + "-thread-" + THREAD_ID.getAndIncrement())
            {
                @Override
                public void run()
                {
                    try
                    {
                        //把鏈路追蹤設置到線程池中的線程
                        if (null != map)
                        {
                            MDC.getMDCAdapter().setContextMap(map);
                        }
                        super.run();
                    }
                    finally
                    {
                        //使用完畢后,清理緩存,避免內存溢出
                        MDC.clear();
                    }
                }
            };
            return t;
        }
    }
    
  • 同樣,把異步任務池的線程池也換成我們自定義的線程池。擴展異步任務池的配置服務ThreadPoolConfigurer 如下:
    @Configuration
    @EnableAsync
    public class ThreadPoolConfigurer implements AsyncConfigurer
    {
        @Override
        public Executor getAsyncExecutor()
        {
            return CommonThreadPool.getExecutor("asyncPool", CORE_NUM, MAX_NUM);
        }
    }
    
  • 至此,基于Tomcat的SpringBoot鏈路追蹤全部改造完畢。

5. Spring-Security-OAuth2-Authorization-Server引入zipkin

  • 本來最開始使用的是oauth2框架是Spring Authorization Server,很不幸該項目2021年被下線了,考慮到該框架不可維護,需要替換成繼任者Spring-Security-OAuth2-Authorization-Server,但是二者的代碼差異非常大;
  • 項目一直使用的JDK是1.8,使用的SpringBoot/SpringCloud版本為2.7.x+/3.1.x+,可支持JDK1.8的Spring-Security-OAuth2-Authorization-Server最高版本只有0.2.3,而0.2.3最多支持的SpringBoot/SpringCloud版本為2.5.x+/3.0.x+,而兩個版本的SpringBoot/SpringCloud存在較大的兼容性問題,搞得人快崩潰了。
  • 嘗試了非常久,才搞定這個版本不匹配的問題。主要思路是在bq-service-auth的根pom 配置去降級SpringBoot/SpringCloud的版本,配置如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://maven.apache.org/POM/4.0.0"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <packaging>pom</packaging>
    
        <artifactId>bq-service-auth</artifactId>
        <version>1.0.0</version>
    
        <dependencyManagement>
            <dependencies>
                <!--引入基礎依賴-->
                <dependency>
                    <groupId>com.biuqu</groupId>
                    <artifactId>bq-parent</artifactId>
                    <version>${bq.version}</version>
                    <scope>import</scope>
                </dependency>
    
                <!---引入微服務基礎jar,并把高版本spring cloud和springboot換成低版本-->
                <dependency>
                    <groupId>com.biuqu</groupId>
                    <artifactId>bq-boot-base</artifactId>
                    <version>1.0.4</version>
                    <exclusions>
                        <exclusion>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-autoconfigure</artifactId>
                        </exclusion>
                        <exclusion>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-actuator-autoconfigure</artifactId>
                        </exclusion>
                        <exclusion>
                            <groupId>org.springframework.cloud</groupId>
                            <artifactId>spring-cloud-starter-sleuth</artifactId>
                        </exclusion>
                        <exclusion>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-devtools</artifactId>
                        </exclusion>
                        <exclusion>
                            <groupId>org.springframework.cloud</groupId>
                            <artifactId>spring-cloud-sleuth-zipkin</artifactId>
                        </exclusion>
                        <exclusion>
                            <groupId>org.springframework.cloud</groupId>
                            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
                        </exclusion>
                        <exclusion>
                            <groupId>org.springframework.cloud</groupId>
                            <artifactId>spring-cloud-sleuth-brave</artifactId>
                        </exclusion>
                        <exclusion>
                            <groupId>org.springframework.cloud</groupId>
                            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
                        </exclusion>
                    </exclusions>
                </dependency>
    
                <!--低版本spring cloud-->
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-sleuth-brave</artifactId>
                    <version>${spring.cloud.security.version}</version>
                    <exclusions>
                        <exclusion>
                            <groupId>io.zipkin.brave</groupId>
                            <artifactId>brave-instrumentation-mongodb</artifactId>
                        </exclusion>
                        <exclusion>
                            <groupId>io.zipkin.brave</groupId>
                            <artifactId>brave-instrumentation-kafka-clients</artifactId>
                        </exclusion>
                        <exclusion>
                            <groupId>io.zipkin.brave</groupId>
                            <artifactId>brave-instrumentation-kafka-streams</artifactId>
                        </exclusion>
                    </exclusions>
                </dependency>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
                    <version>${spring.cloud.security.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-sleuth</artifactId>
                    <version>${spring.cloud.security.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-sleuth-zipkin</artifactId>
                    <version>${spring.cloud.security.version}</version>
                </dependency>
    
                <!--低版本spring boot-->
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter</artifactId>
                    <version>${spring.boot.security.version}</version>
                </dependency>
                <!--為了兼容security-server,此處autoconfigure必須用匹配的低版本-->
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-autoconfigure</artifactId>
                    <version>${spring.boot.security.version}</version>
                    <exclusions>
                        <exclusion>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot</artifactId>
                        </exclusion>
                    </exclusions>
                </dependency>
                <!--為了兼容低版本的autoconfigure,此處actuator必須用匹配的低版本-->
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-actuator-autoconfigure</artifactId>
                    <version>${spring.boot.security.version}</version>
                    <exclusions>
                        <exclusion>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot</artifactId>
                        </exclusion>
                    </exclusions>
                </dependency>
            </dependencies>
        </dependencyManagement>
    </project>
    
  • 驗證的oauth access log日志如下:
    23-06-14 09:36:35.122|0f7969b428e4bb69|0f7969b428e4bb69|0:0:0:0:0:0:0:1|0:0:0:0:0:0:0:1|HTTP/1.1|POST /auth/user/get HTTP/1.1|200|235B|1027ms|-|forward:-|refer:-|PostmanRuntime/7.31.3
    23-06-14 20:24:24.734|9f70c1d26fa9e9aa|9f70c1d26fa9e9aa|0:0:0:0:0:0:0:1|0:0:0:0:0:0:0:1|HTTP/1.1|POST /auth/user/add HTTP/1.1|200|216B|237ms|-|forward:-|refer:-|PostmanRuntime/7.31.3
    23-06-14 20:24:31.246|39705b997ca54c70|39705b997ca54c70|127.0.0.1|127.0.0.1|HTTP/1.1|POST /oauth/token?scope=read&grant_type=client_credentials HTTP/1.1|200|1659B|235ms|-|forward:-|refer:-|PostmanRuntime/7.31.3
    23-06-14 20:38:04.965|714f135ca51d2b65|714f135ca51d2b65|127.0.0.1|127.0.0.1|HTTP/1.1|GET /oauth/jwk HTTP/1.1|200|425B|12ms|-|forward:-|refer:-|Apache-HttpClient/4.5.13 (Java/1.8.0_144)
    23-06-14 20:38:59.282|bee49f5e7bdfe536|708815f91d8f5fdf|127.0.0.1|127.0.0.1|HTTP/1.1|POST /oauth/token?scope=read&grant_type=client_credentials HTTP/1.1|200|1659B|220ms|-|forward:127.0.0.1|refer:-|PostmanRuntime/7.31.3
    

    bq-service-auth其實是擴展源碼最多的的一個開源代碼,后續(xù)再單獨講述。

6. Spring-Cloud-Gateway引入zipkin

  • Spring-Cloud-Gateway是基于Spring-Boot-WebFlux,而Spring-Boot-WebFlux是基于Netty Web容器的,與Tomcat容器的日志配置差異較大。
  • 基于WebFlux的服務在啟動時,需要添加啟動參數(shù)-Dreactor.netty.http.server.accessLogEnabled=true -Dproject.name=bq-gateway;
  • 網關的logback-spring.xml 配置差異部分如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <!--日志級別以及優(yōu)先級排序: FATAL > ERROR > WARN > INFO > DEBUG-->
    <configuration debug="false">
        <!--控制臺日志-->
        <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${LOG_PATTERN}</pattern>
                <charset>UTF-8</charset>
            </encoder>
        </appender>
      
        <!--spring-cloud-gateway access log-->
        <appender name="accessLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_PATH}/access.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <FileNamePattern>${LOG_PATH}/%d{yy-MM-dd}/access-%d{yy-MM-dd}.log</FileNamePattern>
                <maxHistory>30</maxHistory>
            </rollingPolicy>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${AUDIT_LOG_PATTERN}</pattern>
                <charset>UTF-8</charset>
            </encoder>
        </appender>
        <appender name="asyncAccessLog" class="ch.qos.logback.classic.AsyncAppender">
            <appender-ref ref="accessLog"/>
        </appender>
        <appender name="asyncNettyLog" class="ch.qos.logback.classic.AsyncAppender">
            <appender-ref ref="consoleAppender"/>
            <appender-ref ref="defaultAppender"/>
        </appender>
        <!--gateway access-log, with jvm env:'-Dreactor.netty.http.server.accessLogEnabled=true'-->
        <logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
            <appender-ref ref="asyncAccessLog"/>
        </logger>
        <!--記錄netty日志-->
        <logger name="reactor.netty.http.server.HttpServer" level="DEBUG" additivity="false" includeLocation="true">
            <appender-ref ref="asyncNettyLog"/>
        </logger>
    </configuration>
    
  • 網關的AccessLog工廠NettyConfigurer 定制如下:
    @Slf4j
    @Configuration
    public class NettyConfigurer
    {
        /**
         * 配置自定義的AccessLog
         *
         * @return Netty定制工廠
         */
        @Bean
        public WebServerFactoryCustomizer<NettyReactiveWebServerFactory> nettyServerFactory()
        {
            return factory ->
            {
                //配置access log
                factory.addServerCustomizers(httpServer -> httpServer.accessLog(true, x ->
                {
                    List<String> params = Lists.newArrayList();
                    params.add(x.accessDateTime().format(DateTimeFormatter.ofPattern(TimeUtil.SIMPLE_TIME_FORMAT)));
                    String traceId = Const.MID_LINK;
                    if (null != x.responseHeader(CommonBootConst.TRACE_ID))
                    {
                        traceId = x.responseHeader(CommonBootConst.TRACE_ID).toString();
                    }
                    params.add(traceId);
    
                    String spanId = Const.MID_LINK;
                    if (null != x.responseHeader(CommonBootConst.SPAN_ID))
                    {
                        spanId = x.responseHeader(CommonBootConst.SPAN_ID).toString();
                    }
                    params.add(spanId);
    
                    params.add(x.method().toString());
                    params.add(x.protocol());
                    params.add(x.connectionInformation().remoteAddress().toString());
                    params.add(x.connectionInformation().hostAddress().toString());
                    params.add(x.status() + StringUtils.EMPTY);
                    params.add(x.uri().toString());
                    params.add(x.contentLength() + "B");
                    params.add(x.duration() + "ms");
                    String format = StringUtils.repeat("{}|", params.size());
                    return AccessLog.create(format, params.toArray());
                }));
            };
        }
    }
    
  • 配置完畢后,發(fā)現(xiàn)日志只在第一個網關過濾器中有TraceId,后面的過濾器都沒有了,因此想到添加切面NettyTraceLogAop 來實現(xiàn)過濾器間的傳遞:
    @Slf4j
    @Component
    @Aspect
    public class NettyTraceLogAop extends BaseAop
    {
        @Before(BEFORE_PATTERN)
        @Override
        public void before(JoinPoint joinPoint)
        {
            super.before(joinPoint);
        }
    
        @Override
        protected void doBefore(Method method, Object[] args)
        {
            Object webServerObj = args[0];
            if (webServerObj instanceof ServerWebExchange)
            {
                ServerWebExchange exchange = (ServerWebExchange)webServerObj;
                MDCAdapter mdc = MDC.getMDCAdapter();
                Map<String, String> map = mdc.getCopyOfContextMap();
                if (!MapUtils.isEmpty(map))
                {
                    //獲取并緩存鏈路信息
                    exchange.getAttributes().put(GatewayConst.TRACE_LOG_KEY, map);
                    HttpHeaders headers = exchange.getResponse().getHeaders();
                    //把鏈路信息緩存至exchange的response對象header
                    for (String traceKey : map.keySet())
                    {
                        String value = map.get(traceKey);
                        if (!headers.containsKey(traceKey))
                        {
                            headers.add(traceKey, value);
                        }
                    }
                }
                else
                {
                    //從緩存中提取并設置給過濾器
                    Map<String, String> cachedMap = exchange.getAttribute(GatewayConst.TRACE_LOG_KEY);
                    if (!MapUtils.isEmpty(cachedMap))
                    {
                        mdc.setContextMap(cachedMap);
                    }
                }
            }
        }
    
        /**
         * 攔截所有過濾器匹配表達式
         */
        private static final String BEFORE_PATTERN = "(execution (* com.biuqu.boot.*.*.filter.*GatewayFilter.filter(..)))";
    }
    

    當前的策略是從MDC中獲取然后放入全局的參數(shù)中去,也可以不放入Header頭。文章來源地址http://www.zghlxwxcb.cn/news/detail-487372.html

  • 測試過程中發(fā)現(xiàn),后面會重復出現(xiàn)前面出現(xiàn)過的TraceId,因此在最后一個過濾器RemovingGatewayFilter 中清除下整個鏈路的緩存,包括MDC的緩存;
    @Slf4j
    @Component
    public class RemovingGatewayFilter implements GlobalFilter, Ordered
    {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
        {
            //從緩存中提取并設置給過濾器
            Map<String, String> cachedMap = exchange.getAttribute(GatewayConst.TRACE_LOG_KEY);
            return chain.filter(exchange).doFinally(s ->
            {
                if (!MapUtils.isEmpty(cachedMap))
                {
                    MDC.getMDCAdapter().setContextMap(cachedMap);
                }
    
                long start = System.currentTimeMillis();
                Map<String, Object> attributes = exchange.getAttributes();
                if (attributes.containsKey(GatewayConst.TRACE_LOG_KEY))
                {
                    attributes.remove(GatewayConst.TRACE_LOG_KEY);
                }
                log.info("finally cost:{}ms", System.currentTimeMillis() - start);
                MDC.getMDCAdapter().clear();
            });
        }
    }
    
  • 網關的Access Log運行效果如下:
23-06-14 20:38:21.160|063e2bc1e5223c6d|063e2bc1e5223c6d|POST|HTTP/1.1|/127.0.0.1:63787|/127.0.0.1:9992|500|/oauth/token?scope=read&grant_type=client_credentials|54B|225ms|
23-06-14 20:38:59.003|bee49f5e7bdfe536|bee49f5e7bdfe536|POST|HTTP/1.1|/127.0.0.1:63787|/127.0.0.1:9992|200|/oauth/token?scope=read&grant_type=client_credentials|1647B|304ms|
23-06-14 20:39:41.736|77b9d62ebaafec48|77b9d62ebaafec48|POST|HTTP/1.1|/127.0.0.1:63787|/127.0.0.1:9992|200|/oauth/token?scope=read&grant_type=client_credentials|1673B|251ms|
23-06-14 20:40:55.359|b2d83c74c2911355|b2d83c74c2911355|POST|HTTP/1.1|/0:0:0:0:0:0:0:1:63867|/0:0:0:0:0:0:0:1:9992|200|/oauth/token?scope=read&grant_type=client_credentials|1673B|239ms|
23-06-14 20:41:10.096|0637881570dec847|0637881570dec847|POST|HTTP/1.1|/0:0:0:0:0:0:0:1:63867|/0:0:0:0:0:0:0:1:9992|500|/oauth/enc/token?scope=read&grant_type=client_credentials|53B|11ms|

7. 參考資料

  • [1] SkyWalking 鏈路追蹤
  • [2] Pinpoint 應用性能管理工具
  • [3] 使用Pinpoint作分布式鏈路跟蹤系統(tǒng)
  • [4] 從MDC說分布式鏈路追蹤的前世今生
  • [5] Java學習錄
  • [6] Spring Authorization Server 正式遷移至 spring-projects

到了這里,關于鏈路追蹤在開源SpringBoot/SpringCloud微服務框架的實踐的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • OAuth2在開源SpringBoot/SpringCloud微服務框架的最佳實踐

    前期內容導讀: Java開源RSA/AES/SHA1/PGP/SM2/SM3/SM4加密算法介紹 Java開源AES/SM4/3DES對稱加密算法介紹及其實現(xiàn) Java開源AES/SM4/3DES對稱加密算法的驗證說明 Java開源RSA/SM2非對稱加密算法對比介紹 Java開源RSA非對稱加密算法實現(xiàn) Java開源SM2非對稱加密算法實現(xiàn) Java開源接口微服務代碼框架

    2024年02月11日
    瀏覽(18)
  • 熔斷降級與限流在開源SpringBoot/SpringCloud微服務框架的最佳實踐

    熔斷降級與限流在開源SpringBoot/SpringCloud微服務框架的最佳實踐

    前期內容導讀: Java開源RSA/AES/SHA1/PGP/SM2/SM3/SM4加密算法介紹 Java開源AES/SM4/3DES對稱加密算法介紹及其實現(xiàn) Java開源AES/SM4/3DES對稱加密算法的驗證說明 Java開源RSA/SM2非對稱加密算法對比介紹 Java開源RSA非對稱加密算法實現(xiàn) Java開源SM2非對稱加密算法實現(xiàn) Java開源接口微服務代碼框架

    2024年02月12日
    瀏覽(25)
  • 服務鏈路追蹤 —— SpringCloud Sleuth

    隨著業(yè)務的發(fā)展,系統(tǒng)規(guī)模變得越來越大,微服務拆分越來越細,各微服務間的調用關系也越來越復雜。客戶端請求在后端系統(tǒng)中會經過多個不同的微服務調用來協(xié)同產生最后的請求結果,幾平每一個請求都會形成一個復雜的分布式服務調用鏈路,在每條鏈路中任何一個依賴

    2024年02月08日
    瀏覽(18)
  • 【微服務】springcloud集成skywalking實現(xiàn)全鏈路追蹤

    目錄 一、前言 二、環(huán)境準備 2.1 軟件環(huán)境 2.2?微服務模塊 2.3 環(huán)境搭建

    2024年02月03日
    瀏覽(16)
  • SpringCloud搭建微服務之Micrometer分布式鏈路追蹤

    SpringCloud搭建微服務之Micrometer分布式鏈路追蹤

    由于Spring Cloud Sleuth最新版本只支持Spring Boot 2.7.x,核心項目已經遷移到Micrometer Traceing項目,Spring Boot 3.x版本要實現(xiàn)分布式鏈路追蹤需要集成Micrometer。更多詳情可以參閱Micrometer官網 本文將以Spring Boot 3.2.x和Spring Cloud 2023.0.x版本和JDK 17實現(xiàn)分布式鏈路追蹤,有需要了解Spring Bo

    2024年03月22日
    瀏覽(24)
  • 【微服務】springcloud集成sleuth與zipkin實現(xiàn)鏈路追蹤

    目錄 一、前言 二、分布式鏈路調用問題 三、鏈路追蹤中的幾個概念

    2024年01月22日
    瀏覽(16)
  • 全網最全的微服務鏈路追蹤實踐-SkyWalking(看這一篇就夠了)

    全網最全的微服務鏈路追蹤實踐-SkyWalking(看這一篇就夠了)

    鏈路追蹤介紹 對于一個大型的幾十個、幾百個微服務構成的微服務架構系統(tǒng),通常會遇到下面一些問題,比如: 1. 如何串聯(lián)整個調用鏈路,快速定位問題? 2. 如何縷清各個微服務之間的依賴關系? 3. 如何進行各個微服務接口的性能分折? 4. 如何跟蹤整個業(yè)務流程的調用處

    2024年02月03日
    瀏覽(27)
  • OAuth2在開源SpringBoot微服務框架的實踐

    前期內容導讀: Java開源RSA/AES/SHA1/PGP/SM2/SM3/SM4加密算法介紹 Java開源AES/SM4/3DES對稱加密算法介紹及其實現(xiàn) Java開源AES/SM4/3DES對稱加密算法的驗證說明 Java開源RSA/SM2非對稱加密算法對比介紹 Java開源RSA非對稱加密算法實現(xiàn) Java開源SM2非對稱加密算法實現(xiàn) Java開源接口微服務代碼框架

    2024年02月11日
    瀏覽(18)
  • k8s部署elk+filebeat;springCloud集成elk+filebeat+kafka+zipkin實現(xiàn)多個服務日志鏈路追蹤聚合到es

    k8s部署elk+filebeat;springCloud集成elk+filebeat+kafka+zipkin實現(xiàn)多個服務日志鏈路追蹤聚合到es

    如今2023了,大多數(shù)javaweb架構都是springboot微服務,一個前端功能請求后臺可能是多個不同的服務共同協(xié)做完成的。例如用戶下單功能,js轉發(fā)到后臺 網關gateway服務 ,然后到 鑒權spring-sercurity服務 ,然后到 業(yè)務訂單服務 ,然后到 支付服務 ,后續(xù)還有發(fā)貨、客戶標簽等等服務

    2024年02月16日
    瀏覽(27)
  • day09-SpringCloud Sleuth+Zipkin-鏈路追蹤

    官網:spring-cloud/spring-cloud-sleuth: Distributed tracing for spring cloud (github.com) 分布式鏈路追蹤之Spring Cloud Sleuth+Zipkin最全教程! - bucaichenmou - 博客園 (cnblogs.com) 在微服務框架中,一個由客戶端發(fā)起的請求在后端系統(tǒng)中會經過多個不同的的服務節(jié)點調用,來協(xié)同產生最后的請求結果,

    2024年02月08日
    瀏覽(27)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包