Rubin's Blog

  • 首页
  • 关于作者
  • 隐私政策
享受恬静与美好~~~
分享生活的点点滴滴~~~
  1. 首页
  2. SpringCloud
  3. 正文

SpringCloud之分布式链路追踪

2021年 10月 23日 1044点热度 0人点赞 0条评论

分布式链路追踪技术适用场景

场景描述

为了支撑日益庞大的业务量,我们会使用微服务架构设计我们的系统,使我们的系统不仅能够通过集群部署抵挡流量的冲击,又能根据业务进行灵活的扩展。

那么,在微服务架构下,一次请求少则经过三四次服务调用,多则跨越几十个甚至是上百个服务节点。那么问题就接踵而来:

  1. 如何动态展示服务的调用链路?
  2. 如何分析服务调用链路中的瓶颈节点并对其进行调优?
  3. 如何快速进行服务链路的故障发现?

这就是分布式链路追踪技术存在的意义和目的。

市场上的分布式链路追踪方案

分布式链路追踪技术已然成熟,产品也不少,国内外皆有,比如:

  • Spring Cloud Sleuth + Twitter Zipkin
  • 阿⾥巴巴的“鹰眼”
  • ⼤众点评的“CAT”
  • 美团的“Mtrace”
  • 京东的“Hydra”
  • 新浪的“Watchman”
  • Apache Skywalking

分布式链路追踪技术核心思想

本质:记录日志,作为⼀个完整的技术,分布式链路追踪也有自己的理论和概念。微服务架构中,针对请求处理的调用链可以展现为⼀棵树,示意如下:

上图描述了一个常见的调用场景:一个请求通过网关服务路由到下游的微服务-1,然后微服务-1调用微服务-2,拿到结果后再调用微服务-3,最后组合微服务-2和微服务-3的结果,通过网关返回给用户。

为了追踪整个调用链路,肯定需要记录日志。在日志记录的基础上,还要有一些理论概念。当下主流的的分布式链路追踪技术/系统所基于的理念都来⾃于Google的一篇论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》,主要理论概念如下:

  • Trace:服务追踪的追踪单元是从客户发起请求(request)抵达被追踪系统的边界开始,到被追踪系统向客户返回响应(response)为止的过程
  • Trace ID:为了实现请求跟踪,当请求发送到分布式系统的入口端点时,只需要服务跟踪框架为该请求创建⼀个唯⼀的跟踪标识Trace ID,同时在分布式系统内部流转的时候,框架失踪保持该唯⼀标识,直到返回给请求方。⼀个Trace由⼀个或者多个Span组成,每⼀个Span都有⼀个SpanId, Span中会记录TraceId,同时还有⼀个叫做ParentId,指向了另外⼀个Span的SpanId,表明父子关系,其实本质表达了依赖关系
  • Span ID:为了统计各处理单元的时间延迟,当请求到达各个服务组件时,也是通过⼀个唯⼀标识Span ID来标记它的开始,具体过程以及结束。对每⼀个Span来说,它必须有开始和结束两个节点,通过记录开始Span和结束Span的时间戳,就能统计出该Span的时间延迟,除了时间戳记录之外,它还可以包含⼀些其他元数据,比如时间名称、请求信息等

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表示回复需要的时间(响应的网络延迟)

SpringCloud Sleuth (追踪服务框架)可以追踪服务之间的调用, 间的调用关系及进行问题追踪分析。Sleuth可以记录⼀个服务请求经过哪些服务、服务处理时长等,根据这些,我们能够理清各微服务间的调用关系及进行问题追踪分析。

  • 耗时分析:通过Sleuth了解采样请求的耗时,分析服务性能问题(哪些服务调用比较耗时)
  • 链路优化:发现频繁调用的服务,针对性优化

Sleuth就是通过记录日志的方式来记录踪迹数据的。注意:我们往往把SpringCloud Sleuth 和 Zipkin ⼀起使用,把 Sleuth 的数据信息发送给 Zipkin 进行聚合,利用 Zipkin 存储并展示数据。

Sleuth + Zipkin

我们也是以之前的网关服务为基准,将其改造成被追踪的微服务。我们在其工程中引入依赖坐标:

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

修改配置文件,加入以下配置:

spring:
  zipkin:
    # zipkin-server的请求地址
    base-url: http://zipkin-host:9500
    sender:
      # web 客户端将踪迹日志数据通过网络请求的方式传送到服务端,另外还有配置
      # kafka/rabbit 客户端将踪迹日志数据传递到mq进行中转
      type: web
  sleuth:
    sampler:
      # 采样率 1 代表100%全部采集 ,默认0.1 代表10% 的请求踪迹数据会被采集
      # 生产环境下,请求量非常大,没有必要所有请求的踪迹数据都采集分析,对于网络包括server端压力都是比较大的,可以配置采样率采集一定比例的请求的踪迹数据进行分析即可
      probability: 1
#分布式链路追踪
logging:
  level:
    org.springframework.web.servlet.DispatcherServlet: debug
    org.springframework.cloud.sleuth: debug

我们下面的Zipkin Server服务端模块的定义是在博文SpringCloud Netflix之Eureka Server中搭建的大框架下创建的,有需要的可以参考该博文的大框架的搭建。

我们在spring-cloud-demo模块下创建子模块zipkin-server,其pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-demo</artifactId>
        <groupId>com.rubin</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>zipkin-server</artifactId>

    <dependencies>
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-server</artifactId>
            <version>2.12.3</version>
            <exclusions>
                <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>
        <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>

</project>

启动类:

package com.rubin.zipkin.server;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import zipkin2.server.internal.EnableZipkinServer;

@SpringBootApplication
@EnableZipkinServer
@EnableTransactionManagement
public class ZipkinServerBootstrap {

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

}

新建一个名称叫做zipkin的数据库,导入下面的SQL:

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,
    `remote_service_name` VARCHAR(255),
    `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',
    PRIMARY KEY (`trace_id_high`, `trace_id`, `id`)
) ENGINE = InnoDB
  ROW_FORMAT = COMPRESSED
  CHARACTER SET = utf8
  COLLATE utf8_general_ci;
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 (`remote_service_name`) COMMENT 'for getTraces and getRemoteServiceNames';
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 and autocomplete values';
ALTER TABLE zipkin_annotations ADD INDEX (`a_key`) COMMENT 'for getTraces and autocomplete values';
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,
    PRIMARY KEY (`day`, `parent`, `child`)
) ENGINE = InnoDB
  ROW_FORMAT = COMPRESSED
  CHARACTER SET = utf8
  COLLATE utf8_general_ci;

创建配置文件:

server:
  port: 9500

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://zipkin_db:3306/zipkin?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: 123456
    druid:
      initialSize: 10
      minIdle: 10
      maxActive: 30
      maxWait: 50000

# 指定zipkin持久化介质为mysql
zipkin:
  storage:
    type: mysql

management:
  metrics:
    web:
      server:
        # 关闭自动检测请求
        auto-time-requests: false

我们按照 网关 服务的改造方式改造我们的服务消费者和服务提供者。启动我们的服务,发起调用就可以在Zipkin Server的页面中查看我们的调用链路以及调用时长等等。

至此,我们本博文的内容就这些了。欢迎小伙伴们积极留言交流~~~

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: SpringCloud
最后更新:2022年 6月 9日

RubinChu

一个快乐的小逗比~~~

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复
文章目录
  • 分布式链路追踪技术适用场景
    • 场景描述
    • 市场上的分布式链路追踪方案
  • 分布式链路追踪技术核心思想
  • Sleuth + Zipkin
最新 热点 随机
最新 热点 随机
问题记录之Chrome设置屏蔽Https禁止调用Http行为 问题记录之Mac设置软链接 问题记录之JDK8连接MySQL数据库失败 面试系列之自我介绍 面试总结 算法思维
Dubbo之配置项说明 数据结构之图 MongoDB之体系结构 Kafka高级特性之稳定性 Tomcat之源码环境搭建 SpringBoot之配置文件

COPYRIGHT © 2021 rubinchu.com. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

京ICP备19039146号-1