使用sleuth实现微服务跟踪

作者: billJiang | 来源:发表于2017-08-30 18:43 被阅读502次
    sleuth原理图.png
    在微服务架构中,众多的微服务之间互相调用,如何清晰地记录服务的调用链路是一个需要解决的问题。同时,由于各种原因,跨进程的服务调用失败时,运维人员希望能够通过查看日志和查看服务之间的调用关系来定位问题,而Spring cloud sleuth组件正是为了解决微服务跟踪的组件。

    sleuth的原理介绍可以参考这篇文章: 服务链路追踪(Spring Cloud Sleuth)

    本文主要讲解sleuth的两方面用法

    • sleuth+elk 结合,聚合微服务日志
    • sleuth+ zipkin结合,显示文件调用链路

    本文代码参考hello+world+helloworldh+helloworldfeign+track这5个项目

    sleuth+elk聚合日志

    sleuth配置

    • 在微服务项目引入下列依赖
     <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-sleuth</artifactId>
            </dependency>
    
    • 配置文件application.yml增加
    logging:
      level:
        root: INFO
        org.springframework.web.servlet.DispatcherServlet: DEBUG
        org.springframework.cloud.sleuth: DEBUG
    
    
    • 依次启动hello项目helloworld项目,在浏览器输入http://localhost:8020/message后两个项目的控制台输出如下日志
    helloworld日志 hello 日志.png

    其中a7c81616d25c1a88是TraceId,后面跟着的是SpanId,依次调用有一个全局的TraceId,将调用链路串起来。

    查看日志文件并不是一个很好的方法,当微服务越来越多日志文件也会越来越多,通过ELK可以将日志聚合,并进行可视化展示和全文检索。

    sleuth配合elk使用

    ELK是一款日志分析系统,它是Logstash+ElasticSearch+Kibana的技术组合,它可以通过logstash收集各个微服务日志,并通过Kibana进行可视化展示,而且还可以对大量日志信息通过ElasticSearch进行全文检索。

    ELK.png

    操作步骤:

    区别是在启动logstash时,指定了日志来源路径

    /opt/logstash/bin/logstash -e 
    'input { file { codec => json path => "/opt/build/*.json" } } 
    output { elasticsearch { hosts => ["localhost"] } }'
    
    • 项目添加依赖
          <dependency>
                <groupId>net.logstash.logback</groupId>
                <artifactId>logstash-logback-encoder</artifactId>
                <version>4.9</version>
            </dependency>
    
    • 在src/java/resources目录下新建logback-spring.xml,配置如下内容
    <?xml version="1.0" encoding="UTF-8" ?>
    <configuration>
        <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
        <springProperty scope="context"
                        name="springAppName"
                        source="spring.application.name"/>
    
        <property name="LOG_FILE"
                  value="${BUILD_FOLDER:-build}/${springAppName}"/>
    
        <property name="CONSOLE_LOG_PATTERN"
                  value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p})
                  %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan}
                  %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    
        <!-- 控制台输出 -->
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <level>DEBUG</level>
            </filter>
            <encoder>
                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            </encoder>
        </appender>
    
    
        <!-- 按照每天生成日志文件 -->
        <appender name="filelog"  class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_FILE}</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--日志文件输出的文件名-->
                <FileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gz</FileNamePattern>
                <!--日志文件保留天数-->
                <MaxHistory>7</MaxHistory>
            </rollingPolicy>
            <encoder>
                <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
                <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            </encoder>
            <!--日志文件最大的大小-->
            <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                <MaxFileSize>10MB</MaxFileSize>
            </triggeringPolicy>
        </appender>
    
    
        <!-- 使用json格式保存日志文件 -->
        <appender name="jsonlog"  class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_FILE}.json</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--日志文件输出的文件名-->
                <FileNamePattern>${LOG_FILE}.json.%d{yyyy-MM-dd}.gz</FileNamePattern>
                <!--日志文件保留天数-->
                <MaxHistory>7</MaxHistory>
            </rollingPolicy>
            <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
                <providers>
                    <timestamp>
                        <timeZone>UTC</timeZone>
                    </timestamp>
                    <pattern>
                        <pattern>
                            {
                            "severity": "%level",
                            "service": "${springAppName:-}",
                            "trace": "%X{X-B3-TraceId:-}",
                            "span": "%X{X-B3-SpanId:-}",
                            "parent": "%X{X-B3-ParentSpanId:-}",
                            "exportable": "%X{X-Span-Export:-}",
                            "pid": "${PID:-}",
                            "thread": "%thread",
                            "class": "%logger{40}",
                            "rest": "%message"
                            }
                        </pattern>
                    </pattern>
                </providers>
            </encoder>
            <!--日志文件最大的大小-->
            <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
                <MaxFileSize>10MB</MaxFileSize>
            </triggeringPolicy>
        </appender>
    
        <root level="INFO">
            <appender-ref ref="console"/>
            <appender-ref ref="jsonlog"/>
            <appender-ref ref="filelog"/>
        </root>
    
    
    </configuration>
    

    因为上面的日志文件用到spring.application.name,所以需要项目名称的配置挪到bootstrap.yml。

    • 测试结果,在浏览器输入http://localhost:8020/message发起几次调用后,打开http://localhost:5601后看到上述的Kibana页面,说明可以正常使用ELK查询,分析跟踪日志。

    sleuth 结合zipkin

    通过查看日志分析微服务的调用链路并不是一个很直观的方案,结合zipkin可以很直观地显示微服务之间的调用关系。

    trace列表.png trace明细.png 服务依赖关系.png

    通过zipkin可以将调用链路可视化显示。
    下面讲解配置步骤:

    服务端zipkin-server配置

    • 新建项目track,并引入依赖
        <dependency>
                <groupId>io.zipkin.java</groupId>
                <artifactId>zipkin-server</artifactId>
            </dependency>
            <dependency>
                <groupId>io.zipkin.java</groupId>
                <artifactId>zipkin-autoconfigure-ui</artifactId>
         </dependency>
    
    • 启动类添加@EnableDiscoveryClient@EnableZipkinServer注解
    • 配置文件application.yml
    spring:
      application:
        name: sleuth-server
    server:
      port: 9411
    
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/
      instance:
          instanceId: ${spring.application.name}:${spring.cloud.client.ipAddress}:${server.port}
    

    以上服务端就搭建好了

    客户端整合zipkin步骤

    • 客户端添加依赖
             <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zipkin</artifactId>
            </dependency>  
    
    • 配置文件添加
    spring:
      zipkin:
        base-url: http://127.0.0.1:9411
      sleuth:
        sampler:
          percentage: 1.0
    

    指定了zipkin server的地址,下面制定需采样的百分比,默认为0.1,即10%,这里配置1,是记录全部的sleuth信息,是为了收集到更多的数据(仅供测试用)。在分布式系统中,过于频繁的采样会影响系统性能,所以这里配置需要采用一个合适的值。

    zipkin改进

    在这里对zipkin进行改进,主要包含两方面

    • 通过消息中间件收集sleuth数据
    • 持久化sleuth数据

    1、通过消息中间件收集sleuth数据
    通过消息中间件可以将zipkin server和微服务解耦,微服务无需知道zipkin server地址,只需将sleuth数据传入消息中间件。同时,也可以解决zipkin server与微服务网络不通情况。

    改造服务端

    • 修改zipkin server(trace项目)配置
    <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-sleuth</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
            </dependency>
    
        <!--    <dependency>
                <groupId>io.zipkin.java</groupId>
                <artifactId>zipkin-server</artifactId>
            </dependency>-->
            <dependency>
                <groupId>io.zipkin.java</groupId>
                <artifactId>zipkin-autoconfigure-ui</artifactId>
            </dependency>
    
    • 配置文件application.yml增加
      rabbitmq:
        host: localhost
        port: 5673
        username: guest
        password: guest
    

    这是sleuth数据来源

    • 启动类@EnableZipkinServer改为@EnableZipkinStreamServe
      以上服务端改造完毕,下面改造客户端(以helloworld-feign项目为例

    改造客户端
    helloworldfeign项目为例

    • 修改依赖
      <!-- 使用消息中间件后,不再直接和zipkin server-->
           <!-- <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zipkin</artifactId>
            </dependency>-->
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-sleuth-stream</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-sleuth</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
            </dependency>
    
    • 配置文件增加
    spring:
      rabbitmq:
        host: localhost
        port: 5673
        username: guest
        password: guest
    

    并删除下面指向zipkin server的配置

    spring:
      zipkin:
        base-url: http://127.0.0.1:9411
    

    这样就完成了客户端的配置,依次启动trackhelloworldfeign项目,通过http://localhost:8030/message调用其他服务后,在zipkin server 成功获取了helloworldfeign的sleuth数据。

    在以上的过程中,只要重启zipkin server,发现之前的数据丢失。这是因为zipkin server获取的数据是放在内存的,我们可以获取的服务追踪数据放入到ElasticSearch

    2、持久化sleuth数据

    • 修改依赖
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
            </dependency>
    
            <!-- 持久化到ElasticSearch-->
            <!--<dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-sleuth</artifactId>
            </dependency>-->
            <dependency>
                <groupId>io.zipkin.java</groupId>
                <artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
                <version>1.29.2</version>
            </dependency>
    
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
            </dependency>
    
               <dependency>
                <groupId>io.zipkin.java</groupId>
                <artifactId>zipkin-autoconfigure-ui</artifactId>
            </dependency>
    
    • 配置文件增加
    zipkin:
      storage:
        type: elasticsearch
        elasticsearch:
          cluster: elasticsearch
          hosts: http://localhost:9200
          index: zipkin
          index-shards: 5
          index-replicas: 1
    

    再次启动track项目后,可以将数据持久化到elasticsearch。

    相关文章

      网友评论

      • 6f1d6d851d2d:为什么我的zipkin开了es存储以后,ui的链图依赖显示不出来了
      • Tommy_MFG:zipkin:
        storage:
        type: elasticsearch
        elasticsearch:
        cluster: elasticsearch
        hosts: http://localhost:9200
        index: zipkin
        index-shards: 5
        index-replicas: 1

        run error:

        java.lang.IllegalStateException: Error processing condition on zipkin.autoconfigure.storage.elasticsearch.http.ZipkinElasticsearchHttpStorageAutoConfiguration.storage

      本文标题:使用sleuth实现微服务跟踪

      本文链接:https://www.haomeiwen.com/subject/njpmdxtx.html