链路追踪

链路追踪

起男 1,369 2021-08-22

链路追踪

分布式链路追踪,就是将一次分布式请求还原成调用链路,进行日志纪录,性能监控,并将一次分布式请求的调用情况集中展示。比如各个服务节点上的耗时、具体请求到哪台机器上、每个节点的请求状态等等

sleuth

相关概念

  • trace:由一组traceId相同的span串联成一个树状结构。为了实现请求跟踪,当请求到达分布式系统的入口端点时,只需要服务跟踪框架为该请求创建一个唯一的标识(即traceId),同时在分布式系统内部流转的时候,框架始终保持该唯一值,直到整个请求的返回。那么我们就可用使用该唯一标识将所有的请求串联起来,形成一条完整的请求链路
  • span:代表了一组基本的工作单元。为了统计各处理单元的延迟,当请求到达各个服务组件的时候,也通过一个唯一标识(spanId)来标记它的开始、具体过程和结束。通过spanId的开始和结束时间戳,就能统计该span的调用时间,除此之外,我们还可以获取如事件的名称,请求信息等元数据
  • annotation:用它纪录一段时间内的事件,内部使用的重要注解
    • cs(client send):客户端发出请求,开始一个请求的生命
    • sr(server received):服务端接收到请求开始处理,sr-cs=网络延迟
    • ss(server send):服务端处理完毕准备发送到客户端,ss-sr=服务器上请求处理时间
    • cr(client):客户端接收到服务端的响应,请求结束。cr-sr=请求的总时间

使用

添加依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>

输出日志格式:

时间 INFO [微服务名称,traceId,spanId,是否将链路结果输出到第三方平台]

zipkin

zipkin是twitter的一个开源项目,它致力于手机服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现

我们可以使用它来收集各个服务器上请求链路的跟踪数据,并通过它提供的rest api接口来辅助我们查询跟踪数据以实现对分布式系统的监控程序,从而及时的发现系统中出现的延迟升高问题并找出系统性能瓶颈的根源

除了面向开发的api接口之外,它也提供了方便的ui组件来帮我们直观的搜索跟踪信息和分析请求链路明细,比如:可以查询某段时间内各用户的请求除了时间等

zipkin提供了可插拔数据存储方式:in-memory、MySQL、Cassandra、elasticsearch

核心组件

  • collector:收集器组件,它主要用于处理从外部系统发送过来的跟踪信息,将这些信息转换为zipkin内部处理的span格式,以支持后续的存储、分析、展示等功能
  • storage:存储组件,它主要用于处理外部系统发送过来的跟踪信息,默认会将这些信息存储在内存中,我们也可以修改此存储策略,通过使用其他组件将跟踪信息存储到数据库中
  • restful api:api组件,它主要用来提供外部访问接口。比如给客户端展示跟踪信息,或是外接系统访问以实现监控等
  • web ui:ui组件,基于api组件实现的上层应用。通过ui组件用户可以方便而又直观的查询和分析跟踪信息

使用

服务端:

  1. 下载jar包
  2. 通过java -jar启动
  3. 通过浏览器访问:http://localhost:9411 进行访问

客户端:

  1. 导入依赖

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zipkin</artifactId>
            </dependency>
    
  2. 添加配置文件

    spring:
      zipkin:
        base-url: http://127.0.0.1:9411/ #zipkin server请求地址
        discoveryClientEnabled: false #让nacos把它当成一个url,而不要当作服务名
      sleuth:
        sampler:
          probability: 1.0 #采样百分比
    

持久化

zipkin service默认会将追踪数据保存到内存,但这种方式不适合生成环境。zipkin支持将追踪数据持久化到mysql数据库或elasticsearch

MySQL:

  1. 创建数据库

    CREATE DATABASE `zipkin`
    
    CREATE TABLE IF NOT EXISTS zipkin_spans (
      `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
      `trace_id` BIGINT NOT NULL,
      `id` BIGINT NOT NULL,
      `name` VARCHAR(255) NOT NULL,
      `parent_id` BIGINT,
      `debug` BIT(1),
      `start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
      `duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query'
    ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
    ALTER TABLE zipkin_spans ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `id`) COMMENT 'ignore insert on duplicate';
    ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`, `id`) COMMENT 'for joining with zipkin_annotations';
    ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';
    ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';
    ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';
    
    CREATE TABLE IF NOT EXISTS zipkin_annotations (
      `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
      `trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',
      `span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',
      `a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',
      `a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
      `a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',
      `a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',
      `endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',
      `endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
      `endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',
      `endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'
    ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
    ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
    ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';
    ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds';
    ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';
    ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces';
    ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces';
    ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';
    
    CREATE TABLE IF NOT EXISTS zipkin_dependencies (
      `day` DATE NOT NULL,
      `parent` VARCHAR(255) NOT NULL,
      `child` VARCHAR(255) NOT NULL,
      `call_count` BIGINT,
      `error_count` BIGINT
    ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
    
    ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);
    
  2. 启动命令添加参数

    java -jar zipkin-server-2.12.9-exec.jar --STORAGE_TYPE=mysql --MYSQL_HOST=MySQL地址 --MYSQL_TCP_PORT=数据库端口号 --MYSQL_DB=数据库名称 --MYSQL_USER=数据库账号 --MYSQL_PASS=数据库密码
    

elasticsearch:

启动命令添加参数

java -jar zipkin-server-2.12.9-exec.jar --STORAGE_TYPE=elasticsearch --ES-HOST=es地址:es端口号