Zipkin进行链路跟踪

2023-11-14

Zipkin 简介

Zipkin 是 Twitter 开源的分布式跟踪系统,基于 Dapper 的论文设计而来。它的主要功能是收集系统的时序数据,从而追踪微服务架构的系统延时等问题。Zipkin 还提供了一个非常友好的界面,便于我们分析追踪数据。

SpringCloudSleuth 简介

通过 SpringCloud 来构建微服务架构,我们可以通过 SpringCloudSleuth 实现分布式追踪,它集成了 Zipkin。

Zipkin 自己需要的是根据场景自定义,但是里面的概念和Sleuth是一致的。

Sleuth 术语

  • span(跨度):基本工作单元。例如,在一个新建的 span 中发送一个 RPC 等同于发送一个回应请求给 RPC,span 通过一个64位 ID 唯一标识,trace 以另一个64位 ID 表示,span 还有其他数据信息,比如摘要、时间戳事件、关键值注释(tags)、span 的 ID,以及进度 ID(通常是 IP 地址)。span 在不断的启动和停止,同时记录了时间信息,当你创建了一个 span,你必须在未来的某个时刻停止它。
  • trace(追踪):一组共享“root span”的 span 组成的树状结构成为 trace。trace 也用一个64位的 ID 唯一标识,trace中的所有 span 都共享该 trace 的 ID。
  • annotation(标注):用来及时记录一个事件的存在,一些核心 annotations 用来定义一个请求的开始和结束。
    - cs,即 Client Sent,客户端发起一个请求,这个 annotion 描述了这个 span 的开始。
    - sr,即 Server Received,服务端获得请求并准备开始处理它,如果将其 sr 减去 cs 时间戳便可得到网络延迟。
    - ss,即 Server Sent,注解表明请求处理的完成(当请求返回客户端),如果 ss 减去 sr 时间戳便可得到服务端需要的处理请求时间。
    - cr,即 Client Received,表明 span 的结束,客户端成功接收到服务端的回复,如果 cr 减去 cs 时间戳便可得到客户端从服务端获取回复的所有所需时间。
  • BinaryAnnotation:提供一些额外信息,一般已key-value对出现

下图演示了请求依次经过 SERVICE1 -> SERVICE2 -> SERVICE3 -> SERVICE4 时,span、trace、annotation 的变化:
在这里插入图片描述

简单的链路追踪实现

(1)在 parent 工程上创建一个子工程:zipkin,在 pom.xml 加入以下依赖:

<dependencies>
     <dependency>
         <groupId>io.zipkin.java</groupId>
         <artifactId>zipkin-autoconfigure-ui</artifactId>
     </dependency>
     <dependency>
         <groupId>io.zipkin.java</groupId>
         <artifactId>zipkin-server</artifactId>
     </dependency>
 </dependencies>

3.编写启动类 Application.java:

@SpringBootApplication
@EnableZipkinServer
public class Application {

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

4.编写配置文件 application.yml:

server:
  port: 9411

启动 Application.java,并访问地址:http://localhost:9411,即可看到如下界面:

在这里插入图片描述
单纯集成 zipkinServer 还达不到追踪的目的,我们还必须使我们的微服务客户端集成 Zipkin 才能跟踪微服务,下面是集成步骤。

(1)在 EurekaClient 工程的 pom 文件中添加以下依赖:

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

创建以下 :

spring:
    zipkin:
        base-url: http://localhost:9411
    sleuth:
        sampler:
            percentage: 1.0

其中,spring.zipkin.base-url 用来指定 zipkinServer 的地址。spring.sleutch.sampler.percentage 用来指定采样请求的百分比(默认为0.1,即10%)。

(3)依次启动注册中心、配置中心、Zipkin、eurekaclient,依次访问 http://localhost:8763/index,http://localhost:9411,进入 Zipkin 界面后,点击 Find a trace 按钮,可以看到 trace 列表:

通过消息中间件实现链路追踪

在之前的实例中,我们使用 HTTP 来收集数据,如果 zipkinServer 的网络地址发生了变化,每个微服务的 base-url 都需要改变,因此,我们还可以通过消息队列来收集追踪数据。

我以 RabbitMQ 作为消息中间件进行演示。

(1)改造 Zipkin 工程,将 pom.xml 依赖修改为:

<dependencies>
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-autoconfigure-ui</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
        </dependency>
    </dependencies>
  Spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
改造 EurekaClient,将 pom.xml 依赖改为如下内容:
<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bus-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-sleuth-stream</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
        </dependency>
    </dependencies>

存储追踪数据

前面的示例中,ZipkinServer 是默认将数据存储在内存中,一旦 ZipkinServer 重启或发生故障,将会导致历史数据丢失,因此我们需要将跟踪数据保存到硬盘中。

ZipkinServer 支持多种后端数据存储,比如:MySQL、ElasticSearch、Cassandra 等。

我以 MySQL 为例来演示如何将历史数据存储在 MySQL 中。

(1)首先创建一个名为 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';

CREATE TABLE IF NOT EXISTS zipkin_dependencies (
  `day` DATE NOT NULL,
  `parent` VARCHAR(255) NOT NULL,
  `child` VARCHAR(255) NOT NULL,
  `call_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)改造 Zipkin 工程并添加以下依赖:

<dependency>
			<groupId>io.zipkin.java</groupId>
			<artifactId>zipkin-server</artifactId>
		</dependency>
		<dependency>
			<groupId>io.zipkin.java</groupId>
			<artifactId>zipkin-autoconfigure-ui</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-sleuth</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
		</dependency>
		<dependency>
			<groupId>io.zipkin.java</groupId>
			<artifactId>zipkin-storage-mysql</artifactId>
			<version>2.4.9</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

(3)在 application.yaml 增加如下配置:

zipkin:
  storage:
    type: mysql
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/zipkin?autoReconnect=true
    username: root
    password: ******
    driverClassName: com.mysql.jdbc.Driver

@SpringBootApplication
@EnableZipkinStreamServer
public class Application {

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

    @Bean
    @Primary
    public MySQLStorage mySQLStorage(DataSource datasource) {
        return MySQLStorage.builder().datasource(datasource).executor(Runnable::run).build();
    }
}

单独使用zipkin

zipkin-server-2.8.3

  • SpanCollector 配置收集器
  • Brave 各工具类的封装,其中builder.traceSampler(Sampler.ALWAYS_SAMPLE)设置采样比率,0-1之间的百分比
  • BraveServletFilter 作为拦截器,需要serverRequestInterceptor,serverResponseInterceptor 分别完成sr和ss操作
  • OkHttpClient 添加拦截器,需要clientRequestInterceptor,clientResponseInterceptor 分别完成cs和cr操作,该功能由 brave中的brave-okhttp模块提供,同样的道理如果需要记录数据库的延迟只要在数据库操作前后完成cs和cr即可,当然brave提供其封装。

核心注入方式

package com.lkl.zipkin.config;

import com.github.kristofa.brave.Brave;
import com.github.kristofa.brave.EmptySpanCollectorMetricsHandler;
import com.github.kristofa.brave.Sampler;
import com.github.kristofa.brave.SpanCollector;
import com.github.kristofa.brave.http.DefaultSpanNameProvider;
import com.github.kristofa.brave.http.HttpSpanCollector;
import com.github.kristofa.brave.okhttp.BraveOkHttpRequestResponseInterceptor;
import com.github.kristofa.brave.servlet.BraveServletFilter;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by liaokailin on 16/7/27.
 */
@Configuration
public class ZipkinConfig {

    @Autowired
    private ZipkinProperties properties;


    @Bean
    public SpanCollector spanCollector() {
        HttpSpanCollector.Config config = HttpSpanCollector.Config.builder()
                .connectTimeout(properties.getConnectTimeout()).readTimeout(properties.getReadTimeout())
                .compressionEnabled(properties.isCompressionEnabled())
                .flushInterval(properties.getFlushInterval()).build();
        return HttpSpanCollector.create(properties.getUrl(), config, new EmptySpanCollectorMetricsHandler());
    }


    @Bean
    public Brave brave(SpanCollector spanCollector) {
        Brave.Builder builder = new Brave.Builder(properties.getServiceName());  //指定state
        builder.spanCollector(spanCollector);
        builder.traceSampler(Sampler.ALWAYS_SAMPLE);
        Brave brave = builder.build();
        return brave;
    }

    @Bean
    public BraveServletFilter braveServletFilter(Brave brave) {
        BraveServletFilter filter = new BraveServletFilter(brave.serverRequestInterceptor(),
                brave.serverResponseInterceptor(), new DefaultSpanNameProvider());
        return filter;
    }

    @Bean
    public OkHttpClient okHttpClient(Brave brave) {
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(new BraveOkHttpRequestResponseInterceptor(brave.clientRequestInterceptor(),
                        brave.clientResponseInterceptor(), new DefaultSpanNameProvider()))
                .build();
        return client;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Zipkin进行链路跟踪 的相关文章

随机推荐

  • jetbrains系列宝藏插件

    pycharm宝藏插件 一 花里胡哨类 1 Background Images Plus设置背景 外链图片转存失败 源站可能有防盗链机制 建议将图片保存下来直接上传 img WLcSsXkm 1650540043327 https cdn
  • Java:在Windows 10和Ubuntu 14.04中配置Java开发环境(jdk-12.0.2)

    在Windows 10中配置Java开发环境 下载Java开发工具包jdk jdk的下载地址为 http www oracle com technetwork java javase downloads index html 点击Java
  • 《矩阵分析》代码Ⅲ——Doolittle分解、Crout分解、Cholesky分解求解线性方程组matlab实现

    矩阵分析 代码 Doolittle分解 Crout分解 Cholesky分解求解线性方程组matlab实现 注意 三种分解方法求解过程都会用到三角矩阵的回代法 小编之前已经写过三角矩阵回代法程序 关于代码可参考 https blog csd
  • 告诉我怎么解决这个问题:输入:sudo ./yolov5 -s ../last.wts ../best.engine s 报错:sudo: ./yolov5:找不到命令...

    这个问题的原因可能是你没有安装 yolov5 或者是你没有指定正确的安装路径 要解决这个问题 你需要检查 yolov5 是否已经正确安装 并确保你在执行命令时使用了正确的路径 如果 yolov5 还没有安装 你需要按照 yolov5 的安装
  • c# 使用udp协议接收消息

    两个例子 例一 引用命名空间 using System Net using System Net Sockets 定义 private UdpClient Reveive IPAddress localIP IPAddress Parse
  • JVisualVM简介与内存泄漏实战分析

    一 JVisualVM能做什么 VisualVM 是Netbeans的profile子项目 已在JDK6 0 update 7 中自带 java启动时不需要特定参数 监控工具在bin jvisualvm exe 能够监控线程 内存情况 查看
  • ubuntu1604 虚拟机启动仅只有桌面文件和壁纸,ctrl+alt+t打不开终端解决办法

    使用ctrl alt f1进入tty命令模式 然后登陆账号密码 最后输入sudo sevice lightdm restart 这样就可以重启桌面 如果还是不行的话就重新装unity sudo apt get update sudo apt
  • 网络__未连接到服务器问题

    网络 未连接到服务器问题 错误提示 1004 未能连接到服务器 或者 1001 请求超时 现象 多次切换网络或者断网 会出现WiFi 内网 下自己的APP中连接不到服务器 但是其他APP可以正常连接 在4G下连接正常 半个小时左右自动连接正
  • 组件分享之后端组件——用于安全高效地构建、更改和版本控制基础架构的工具terraform...

    组件分享之后端组件 用于安全高效地构建 更改和版本控制基础架构的工具terraform 背景 近期正在探索前端 后端 系统端各类常用组件与工具 对其一些常见的组件进行再次整理一下 形成标准化组件专题 后续该专题将包含各类语言中的一些常用组件
  • 有 Docker 谁还在自己本地安装 Mysql ?

    引言 自己在个人电脑上开发项目或脚本时 经常会遇到数据存储的问题 咱们工作项目中 Mysql 是常用的业务数据库 如果要存储数据 难道除了在我们个人电脑安装 Mysql 服务就别无他选了吗 No No 你还有一种选择可以不用在你自己电脑上安
  • vLLM 实战

    引言 随着人工智能技术的飞速发展 以及今年以来 ChatGPT 的爆火 大语言模型 Large Language Model LLM 受到越来越多的关注 为了实现 LLM 部署时的推理优化 全球各地有众多团队做出了各种优化框架 本文以加州大
  • 视频压缩:I帧、P帧、B帧

    说明 1 本文通过整理而来 集多个高手的精华 此为最重点 2 因为在海思平台做多媒体视频处理 所以了解I帧 P帧 B帧等压缩 编解码特点是必须的 3 海思I帧间隔即GOP取值范围 0 1000 以帧为单位 为动态属性 4 欢迎拍砖
  • 2020-3-29 深度学习笔记16 - 结构化概率模型 1 (非结构化建模的挑战-内存要求大/统计销量低/运行时间长)

    第十六章 深度学习中的结构化概率模型 中文 英文 深度学习为研究者们提供了许多建模方式 用以设计以及描述算法 其中一种形式是结构化概率模型的思想 结构化概率模型是许多深度学习重要研究方向的关键组成部分 结构化概率模型使用图来描述概率分布中随
  • UniAPP布局

    uniapp建议使用flex布局 首先定义flex容器 display flex flex容器按主轴排列填充 支持横向 纵向排列 flex属性如下 flex direction排列方向 flex direction row flex wra
  • 上传已有项目到Gitee

    1 创建好gitee仓库 正常新建即可 默认已经创建好了 2 在本地项目目录下 右键 点击git bash here 3 在窗口输入命令 git init 这时候文件夹会多出一个 git文件夹 4 在码云复制仓库地址 然后 在窗口输入命令
  • LeetCode——019

    19 Remove Nth Node From End of List My Submissions QuestionEditorial Solution Total Accepted 104327 Total Submissions 35
  • qt 将图片转为base64字符串html直接可用

    Base64是基于64个可打印字符来显示2进制数据 它用于传输8Bit字节代码 并在HTTP传输中广泛应用 原理 把一连串的二进制数 拆为6个6个的二进制组 然后每个组头部补2个0 从而将数的范围限制在64以内 都是可视化字符了 解码是逆向
  • 抑止TDD noise

    抑止TDD noise 的措施 TDD noise Time Division Distortion 所造成的原因为手机射频发射模块端的功率放大器 Power Amplify 每1 216 8秒会有一个发射讯号产生 在该讯号中包含900MH
  • android opencv卡顿,Opencv读取摄像头卡顿

    Opencv读取摄像头卡顿 开发环境 Win10系统 罗技HD1080p高清摄像头 UE4 21 VS2017后端 开发原理 多线程中经过OpenCV对摄像头进行高清采集 1920 1080 而后显示到UMG中 多线程 遇到问题 在2k采集
  • Zipkin进行链路跟踪

    Zipkin 简介 Zipkin 是 Twitter 开源的分布式跟踪系统 基于 Dapper 的论文设计而来 它的主要功能是收集系统的时序数据 从而追踪微服务架构的系统延时等问题 Zipkin 还提供了一个非常友好的界面 便于我们分析追踪