Spring Cloud Sleuth(十四)

 

作用

再微服务中 服务调用服务很常见。服务中相互调用链路追踪的尤为重要,能够帮助我们再异常时分析出哪个服务出了异常。以及各个链路中相互调用所消耗时间,通过这些数据能够帮助我们分析出各个服务的性能瓶颈

简单例子

在之前的provider和consumer的基础上进行修改

1.consumer接口添加一个日志打印

  @RequestMapping("/findById")
    @ResponseBody
    public User findById(Integer id) {
        logger.info("consumer:findById");
        return userService.findById(id);
    }

2.consumer调用provider的指定接口也添加一个日志打印

 @RequestMapping("/findById")
    @ResponseBody
    public User findById(Integer id) {
        logger.info("provider:findByid");
        return users.stream().filter(c -> c.getId().intValue() == id.intValue()).findAny().get();
    }

访问consumer查看日志打印

现在我们添加sleuth依赖

 <!--sleuth链路跟踪-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>

再观察日志

跟没引入sleuth之前多了这么一串

第一个参数 为服务名对应application.name

第二个参数 叫traceId(它表示一个请求的链路id)

第三个参数 叫spanId(表示链路上的每一个基本的工作单元)

第四个参数 表示是否输出到Zipkin等服务中来收集和展示 这里为false 等后面整合zipKin会用到 

跟踪原理

当请求发送到分布式服务的入口端时(我们这里入口是consumer) 会创建一个TraceId 并在微服务中相互请求时通过报文head一直流转下去 通过TraceId能够实现整条链路的跟踪

为了统计每个单元的处理时间每个单元都有一个spanId spanId记录了请求开始时间戳和请求结束时间的时间戳 通过它我们可以知道每个单元的处理时间而找到性能瓶颈

查看更多日志信息

logging:
  level:
    org:
      springframework:
        web:
          servlet:
            DispatcherServlet: DEBUG

参数可以看到更多日志信息

抽样采集

在高并发请求下 如果每个请求都像上面一样记录日志并保存起来 会对性能产生影响 所以sleuth提供一个阀值 默认是0.1 10个请求只抽取一个来记录日志用于性能分析

通过设置以下参数

spring:
  sleuth:
    sampler:
      probability: 1 #采集量 默认0.1 1为100% 但是会对性能影响 测试阶段使用

调试阶段可设置为1.上线后慢慢减少

自定义抽样采集

sleuth的抽样采集是使用Sampler 接口实现的 我们可以自定义

public abstract class Sampler {
    public abstract boolean isSampled(long var1);

    public static Sampler create(float rate) {
        return CountingSampler.create(rate);
    }
}

默认是使用PercentageBasedSarnpler 我们可以通过实现Sampler自定义抽样采集 并通过部署

整合Logstash 

在分布式上 我们的服务是集群的同时各个微服务散落在不同的服务器上。如果我们需要分析日志是很困难和繁琐的我们可以通过Logstash对日志进行收集

1.在consumer和provider添加pom依赖

<dependency>
            <groupId>net.logstash.logback</groupId>
            <artifactId>logstash-logback-encoder</artifactId>
            <version>5.1</version>
        </dependency>

2.resources添加一个名为logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--该日志将日志级别不同的log信息保存到不同的文件中 -->
<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="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){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>INFO</level>
        </filter>
        <!-- 日志输出编码 -->
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- 为logstash输出的JSON格式的Appender -->
    <appender name="logstash"
              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>3</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:-}",
                        "exportable": "%X{X-Span-Export:-}",
                        "pid": "${PID:-}",
                        "thread": "%thread",
                        "class": "%logger{40}",
                        "rest": "%message"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
    </appender>

    <!-- 日志输出级别 -->
    <root level="INFO">
        <appender-ref ref="console" />
        <appender-ref ref="logstash" />
    </root>
</configuration>

这里我们只是看效果 在项目下输出一个json文件  可以指定输出到mysql  stash服务端 输出到ElasticSearch  相应配置百度

启动访问会在项目目录下输出json文件里面就包含日志信息

遇到过的问题

没有日志输出(排查是日志文件没生效)

https://www.jb51.net/article/132619.htm

启动报错

Exception in thread "main" java.lang.AbstractMethodError

at ch.qos.logback.core.OutputStreamAppender.encoderInit(OutputStreamAppender.java:180)

at ch.qos.logback.core.OutputStreamAppender.setOutputStream(OutputStreamAppender.java:171)

at ch.qos.logback.core.FileAppender.openFile(FileAppender.java:206)

at ch.qos.logback.core.FileAppender.start(FileAppender.java:127)

at ch.qos.logback.core.rolling.RollingFileAppender.start(RollingFileAppender.java:100)

at ch.qos.logback.core.joran.action.AppenderAction.end(AppenderAction.java:90)

at ch.qos.logback.core.joran.spi.Interpreter.callEndAction(Interpreter.java:309)

at ch.qos.logback.core.joran.spi.Interpreter.endElement(Interpreter.java:193)

at ch.qos.logback.core.joran.spi.Interpreter.endElement(Interpreter.java:179)

at ch.qos.logback.core.joran.spi.EventPlayer.play(EventPlayer.java:62)

at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:165)

at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:152)

at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:110)

at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:53)

at ch.qos.logback.classic.util.ContextInitializer.configureByResource(ContextInitializer.java:75)

at ch.qos.logback.classic.util.ContextInitializer.autoConfig(ContextInitializer.java:150)

at org.slf4j.impl.StaticLoggerBinder.init(StaticLoggerBinder.java:84)

at org.slf4j.impl.StaticLoggerBinder.<clinit>(StaticLoggerBinder.java:55)

at org.slf4j.LoggerFactory.bind(LoggerFactory.java:150)

at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:124)

at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:412)

at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:357)

at org.apache.logging.slf4j.SLF4JLoggerContext.getLogger(SLF4JLoggerContext.java:39)

at org.apache.commons.logging.LogFactory$Log4jLog.<init>(LogFactory.java:204)

at org.apache.commons.logging.LogFactory$Log4jDelegate.createLog(LogFactory.java:166)

at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:109)

at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:99)

at org.springframework.boot.SpringApplication.<clinit>(SpringApplication.java:198)

at com.liqiang.provider.SpringCloudProviderApplication.main(SpringCloudProviderApplication.java:12)

 

改版本

整合Zipkin 

猜你喜欢

转载自www.cnblogs.com/LQBlog/p/10396832.html