• springcloud日志链路追踪,Zipkin,Spring Cloud Sleuth


    前言

    分布式系统中,系统往往被拆分成许多模块单独部署,为了响应一个客户端请求,服务端可能需要调用多个上游服务。此种多服务调用模式会增加故障排查的难度,针对这种问题可以通过分布式链路追踪技术来解决。

    举例

    样例介绍

    设想一个最简单的分布式微服务调用,consumer服务通过feign调用provider服务的接口来响应客户端请求(架构图如下),日志采用logback。
    架构图

    链路追踪实现

    • 利用slf4j的MDC为应用每一个请求写入唯一流水号,如果当前请求头已包含流水号则使用该流水号(基于threadLocal),以便上游provider的流水号与consumer相同。
    /**
     * @description http请求拦截
     */
    public class RequestInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String traceId = request.getHeader(CfgConst.TRACE_KEY);
            if (traceId == null){
                traceId = UUID.randomUUID().toString().replace("-", "");
            }
            MDC.put(CfgConst.TRACE_KEY, traceId);
            return true;
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            MDC.remove(CfgConst.TRACE_KEY);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 为feign的所有request的请求头添加流水号,以便consumer将流水号发送到provider。
    /**
     * @description feign拦截器,设置traceId
     */
    @Component
    public class FeignInterceptor implements RequestInterceptor {
    
        @Override
        public void apply(RequestTemplate requestTemplate) {
            String key = CfgConst.TRACE_KEY;
            String traceId = MDC.get(key);
            if ( !StringUtils.isEmpty(traceId) ){
                requestTemplate.header(CfgConst.TRACE_KEY, traceId);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 在logback配置文件配置流水号输出
    
        <springProfile name="dev">
            <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
                <encoder>
                    <pattern>%d{HH:mm:ss} [%thread] %-5level |%X{traceId}| %logger{36} - %msg%npattern>
                encoder>
            appender>
    
            <root level="DEBUG">
                <appender-ref ref="CONSOLE"/>
            root>
        springProfile>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • consumer将流水号统一返回给客户端
    /**
     * @description 统一封装返回格式,带上统一日志追踪
     */
    @RestControllerAdvice
    @Slf4j
    public class ResultAdvice implements ResponseBodyAdvice {
        @Resource
        private ObjectMapper objectMapper;
    
        @Override
        public boolean supports(MethodParameter returnType, Class converterType) {
            return true;
        }
    
        @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
            JsonResult jsonResult = JsonResult.success(body);
            String traceId = MDC.get(CfgConst.TRACE_KEY);
            jsonResult.setTraceId(traceId);
    
            if (body instanceof String){
                try {
                    return objectMapper.writeValueAsString(jsonResult);
                } catch (JsonProcessingException e) {
                    log.error("system error", e);
                    return e.getMessage();
                }
            }
            return jsonResult;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    链路追踪效果

    • 浏览器访问consumer接口得到流水号
      在这里插入图片描述
    • 通过浏览器获取的流水号去consumer查询
      在这里插入图片描述
    • 通过浏览器获取的流水号去上游服务provider查询
      在这里插入图片描述

    Sleuth & Zinkin

    介绍

    • Spring Cloud Sleuth为Spring Cloud提供了分布式追踪解决方案
    • Spring Cloud Sleuth 提供了以下功能:
      • 链路追踪:通过 Sleuth 可以很清楚地看出一个请求都经过了那些服务,可以很方便地理清服务间的调用关系
      • 性能分析:通过 Sleuth与Zinkin可以很方便地看出每个请求的耗时,分析哪些服务调用比较耗时
      • 数据分析,优化链路:对于频繁调用一个服务,或并行调用等,可以针对业务做一些优化
      • 可视化错误:对于程序未捕获的异常,可以搭配Zipkin 查看
    • Sleuth术语介绍
      • span:基本工作单位,一次单独的调用链可以称为一个span,例如后台接口向上游微服务发起一次socket数据请求到接收到响应数据就是一个span
      • trace:一系列 span 组成的树状结构,一个 trace 是一次完整的链路,内部包含多个span,例如客户端发起一个请求到收到响应数据。trace 和 span 存在一对多的关系,span 与 span 之间存在父子关系
    • Sleuth官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-sleuth/2.1.3.RELEASE/single/spring-cloud-sleuth.html#_json_logback_with_logstash
    • Zinkin可以用来收集各个服务上请求链路的追踪数据,并通过它提供的 REST API 接口来辅助我们查询追踪数据以实现对分布式系统的监控

    使用

    • 以上面搭建的工程为例,为consumer与provider引入maven依赖
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-sleuthartifactId>
        <version>2.1.3.RELEASEversion>
    dependency>
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-zipkinartifactId>
        <version>2.1.3.RELEASEversion>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    java -jar zipkin-server-xxx-exec.jar
    
    • 1
    • consumer与provider应用增加配置
    spring:
      zipkin:
        base-url: http://localhost:9411
        sender:
          #web/kafka/RabbitMQ
          type: web
      sleuth:
        sampler:
          #所有请求都采集
          probability: 1.0
        web:
          enabled: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • logback配置日志输出
    <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} %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>DEBUGlevel>
            filter>
            <encoder>
                <pattern>${CONSOLE_LOG_PATTERN}pattern>
                <charset>utf8charset>
            encoder>
        appender>
        <root level="DEBUG">
            <appender-ref ref="console"/>
        root>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 效果
      • 浏览器访问consumer接口得到流水号
        在这里插入图片描述
      • 通过浏览器获取的流水号去consumer查询
        在这里插入图片描述
      • 通过浏览器获取的流水号去上游服务provider查询
        在这里插入图片描述
    • 转到Zinkin 后台查看请求链路的追踪数据
      在这里插入图片描述
    • Zipkin默认将追踪数据保存在内存,也支持将追踪数据持久化到 MySQL或 Elasticsearch 中。
  • 相关阅读:
    惊讶,CRUD搬砖两三年了,不会阅读Spring源码?
    在idea中创建MyBatis核心配置文件和映射文件的模板、使用模板搭建MyBatis框架
    Rust语言基础:从Hello World开始
    uniapp app端使用谷歌地图选点定位
    解决“org.apache.catalina.startup.Catalina.stopServer 未配置关闭端口。通过OS信号关闭服务器。服务器未关闭“
    Java快问快答
    Gateway基础知识
    yarn 包管理器设置淘宝镜像和sass镜像
    Vue3使用ElementUI按需自动引入
    GitHub上250K Stars阿里首发Java并发编程
  • 原文地址:https://blog.csdn.net/qq_41633199/article/details/127482748