微服务监控之分布式链路追踪技术 Sleuth + Zipkin

1,486 阅读6分钟

一、分布式链路追踪技术适⽤场景(问题场景)

1. 场景描述

在微服务架构下,⼀次请求少则经过三四次服务调⽤完成,多则跨越⼏⼗个甚⾄是上百个服 务节点。那么问题接踵⽽来:

  • 如何动态展示服务的调⽤链路?(⽐如A服务调⽤了哪些其他的服务---依赖关系)
  • 如何分析服务调⽤链路中的瓶颈节点并对其进⾏调优?(⽐如A—>B—>C,C服务处理时间特别⻓)
  • 如何快速进⾏服务链路的故障发现?

2. 分布式链路追踪技术

分布式环境下基于这种想法实现的监控技术就是就是分布式链路追踪(全链路追踪)

3. 市场上的分布式链路追踪⽅案

  • Spring Cloud Sleuth + Twitter Zipkin(社区最活跃、文档最丰富)
  • 阿⾥巴巴的“鹰眼”
  • ⼤众点评的“CAT”
  • 美团的“Mtrace”
  • 京东的“Hydra”
  • 新浪的“Watchman”
  • Apache Skywalking(最近被提到很多)

二、分布式链路追踪技术核⼼思想

本质:记录⽇志,作为⼀个完整的技术,分布式链路追踪也有⾃⼰的理论和概念

微服务架构中,针对请求处理的调⽤链可以展现为⼀棵树,示意如下:

上图标识⼀个请求链路,⼀条链路通过TraceId唯⼀标识,span标识发起的请求信息,各span通过parrentId关联起来

Trace: request-> response 整个过程

Trace ID: 服务跟踪框架为该请求 创建⼀个唯⼀的跟踪标识Trace ID,同时在分布式系统内部流转的时候,框架失踪保持该唯⼀标识,直到返回给请求⽅

Span ID: 为了统计各处理单元的时间延迟,当请求到达各个服务组件时,也是通过⼀个唯⼀标识SpanID来标记它的开始,具体过程以及结束。对每⼀个Span来说,它必须有开始和结束两个节点,通过记录开始Span和结束Span的时间戳,就能统计出该Span的时间延迟,除了时间戳记录之外,它还可以包含⼀些其他元数据,⽐如时间名称、请求信息等。每⼀个Span都会有⼀个唯⼀跟踪标识 Span ID,若⼲个有序的 span 就组成了⼀个 trace。


Span可以认为是⼀个⽇志数据结构,在⼀些特殊的时机点会记录了⼀些⽇志信息,⽐如有时间戳、spanId、TraceId,parentIde等,Span中也抽象出了另外⼀个概念,叫做事件,核⼼事件如下:

  • CS :client send/start 客户端/消费者发出⼀个请求,描述的是⼀个span开始
  • SR: server received/start 服务端/⽣产者接收请求 SR-CS属于请求发送的⽹络延迟
  • SS: server send/finish 服务端/⽣产者发送应答 SS-SR属于服务端消耗时间
  • CR:client received/finished 客户端/消费者接收应答 CR-SS表示回复需要的时间(响应的⽹络延迟)

注意:我们往往把Spring Cloud Sleuth 和 Zipkin ⼀起使⽤,把 Sleuth 的数据信息发送给 Zipkin 进⾏聚合,利⽤ Zipkin 存储并展示数据。

三、Sleuth + Zipkin

  1. 每⼀个需要被追踪踪迹的微服务⼯程都引⼊依赖坐标
<!--链路追踪-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
  1. 每⼀个微服务都修改application.yml配置⽂件,添加⽇志级别
#分布式链路追踪
logging:
  level:
    org.springframework.web.servlet.DispatcherServlet: debug
    org.springframework.cloud.sleuth: debug

请求到来时,我们在控制台可以观察到 Sleuth 输出的⽇志(全局 TraceId、SpanId等)。

  1. 结合 Zipkin 展示追踪数据

Zipkin 包括Zipkin Server和 Zipkin Client两部分,Zipkin Server是⼀个单独的服务,Zipkin Client就是具体的微服务

Zipkin Server 构建 :

  • pom.xml
<dependencies>
        <!--zipkin-server的依赖坐标-->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-server</artifactId>
            <version>2.12.3</version>
            <exclusions>
                <!--排除掉log4j2的传递依赖,避免和springboot依赖的日志组件冲突-->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-log4j2</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--zipkin-server ui界面依赖坐标-->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-autoconfigure-ui</artifactId>
            <version>2.12.3</version>
        </dependency>


        <!--zipkin针对mysql持久化的依赖-->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-autoconfigure-storage-mysql</artifactId>
            <version>2.12.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--操作数据库需要事务控制-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>
    </dependencies>
  • ⼊⼝启动类
@SpringBootApplication
@EnableZipkinServer // 开启Zipkin 服务器功能
public class ZipkinServerApplication9411 {

    public static void main(MysqlxDatatypes.Scalar.String[] args) {
        SpringApplication.run(ZipkinServerApplication9411.class,args);
    }

    // 注入事务控制器
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
  • application.yml
server:
  port: 9411
management:
  metrics:
    web:
      server:
        auto-time-requests: false # 关闭自动检测

Zipkin Client 构建 :

  • pom中添加 zipkin 依赖(在具体微服务中修改)
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
  • application.yml 中添加对zipkin server的引⽤
spring:
  application:
    name: lagou-service-autodeliver
  zipkin:
    base-url: http://127.0.0.1:9411/ # zipkin server的请求地址
    sender:
      type: web # 请求方式,默认以http的方式向zipkin server发送追踪数据
  sleuth:
    sampler:
      #采样的百分比,默认为0.1,即10%,这里配置1,是记录全部 的sleuth信息,是为了收集到更多的数据(仅供测试用)。在分布式系统中,过于频繁的采样会影响系 统性能,所以这里配置需要采用一个合适的值。
      probability: 1.0
  • 对于log⽇志,依然保持开启debug状态

Zipkin server ⻚⾯⽅便我们查看服务调⽤依赖关系及⼀些性能指标和异常信息


追踪数据Zipkin持久化到mysql

  • mysql中创建名称为zipkin的数据库,并执⾏如下sql语句(github zipkin stronge 中提供)
  • pom.xml 引入依赖
  <dependency>
      <groupId>io.zipkin.java</groupId>
      <artifactId>zipkin-autoconfigure-storagemysql</
      artifactId>
      <version>2.12.3</version>
      </dependency>
      <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      </dependency>
      <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
      <version>1.1.10</version>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
  </dependency>
  • application.yml
server:
  port: 9411
management:
  metrics:
    web:
      server:
        auto-time-requests: false # 关闭自动检测
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/zipkin?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
    username: root
    password: 123456
    druid:
      initialSize: 10
      minIdle: 10
      maxActive: 30
      maxWait: 50000
# 指定zipkin持久化介质为mysql
zipkin:
  storage:
    type: mysql
  • 启动类中注⼊事务管理器
@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
  return new DataSourceTransactionManager(dataSource);
}

lagouedu 笔记总结

本文使用 mdnice 排版