《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Zipkin/install/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

推荐阅读如下 Zipkin 文章:

1. 概述

Zipkin 是什么?

FROM https://zipkin.io/

Zipkin is a distributed tracing system.
Zipkin 是一个分布式链路追踪系统。

It helps gather timing data needed to troubleshoot latency problems in service architectures.
它可以收集分布式场景下的调用链路数据,用于解决服务之间延迟问题。

Features include both the collection and lookup of this data.
功能包括四部分:链路的收集、存储、查找、展示。

Zipkin UI

和市面上其它分布式链路追踪系统一样,Zipkin 也是根据 Google 论文《Dapper,大规模分布式系统的跟踪系统》作为理论,进行设计。Zipkin 最初由 Twitter 公司开源,而后演化出 Open Zipkin 社区。

如下的「1.1 整体架构」「1.2 流程图」「1.3 基本概念」,参考如下两篇文章改写:

1.1 整体架构

Zipkin 整体架构如下图所示,分成三个部分:整体架构

  • 【红框】Zipkin Tracer :负责从应用中,收集分布式场景下的调用链路数据,发送给 Zipkin Server 服务。
  • 【蓝框】Transport :链路数据的传输方式,目前有 HTTP、MQ 等等多种方式。
  • 【绿框】Zipkin Server :负责接收 Tracer 发送的 Tracing 数据信息,将其聚合处理并进行存储,后提供查询功能。之后,用户可通过 Web UI 方便获得服务延迟、调用链路、系统依赖等等。

下面,我们逐个「1.1.1 Zipkin Tracer」「1.1.2 Transport」「1.1.3 Zipkin Server」小节来瞅瞅。

1.1.1 Zipkin Tracer

Zipkin Tracer(追踪器)驻留在你的应用程序里,并且记录发生操作的时间和元数据。他们经常装配在库上,所以对用户来说是透明的。举个例子,一个装配过的 Web 服务器,会在接收请求和发送响应进行记录。收集的追踪数据叫做 Span(跨度)

一般来说,在 Java 应用程序中,我们使用 Brave 库,作为 Zipkin Server 的 Java Tracer 客户端。同时它的 instrumentation 子项目,已经提供了 SpringMVC、MySQL、Dubbo 等等的链路追踪的功能。

生产环境中的装配器应该是安全并且低负载的。为此,带内(in-band)只传输 ID,并且告诉接收器仍有一个追踪在处理。完成的跨度在带外(out-of-band)汇报给 Zipkin,类似于应用程序异步汇报指标一样。

  • 举个例子,当追踪一个操作的时候,该操作对外发送了一个 HTTP 请求,那么,为了传输 ID 就会添加一些额外的头部信息。头部信息并不是用于发送像是操作明这样的详细信息的。

在上面的 Zipkin 架构图,胖友对可能对 Instrumented Client 和 Instrumented Server 有点懵逼?

  • Instrumented Client 和 Instrumented Server,它们是指分布式架构中使用了 Tracer 工具的两个应用,Client 会调用 Server 提供的服务,两者都会向 Zipkin 上报链路数据。
  • Non-Instrumented Server,指的是未使用 Trace 工具的 Server,显然它不会上报链路数据,但是调用其的 Instrumented Client 还是会上报链路数据。

1.1.2 Transport

在装配应用中,用于向 Zipkin 发送数据的组件叫做 Reporter。Reporter 通过 Transport 发送追踪数据到 Zipkin 的 Collector,Collector 持久化数据到 Storage 中。之后,API 从 Storage 中查询数据提供给 UI。

一般来说,在 Java 应用程序中,我们使用 zipkin-reporter-java 库,作为 Zipkin Reporter 客户端。包括如下的 Transport 方式:

请求量级小的时候,采用 HTTP 传输即可。量较大的时候,推荐使用 Kafka 消息队列。

1.1.3 Zipkin Server

Zipkin Server 包括 Collector、Storage、API、UI 四个组件。

① Collector

Collector 负责接收 Tracer 发送的链路数据,并为了后续的查询,对链路数据进行校验、存储、索引。

② Storage

Storage 负责存储链路数据,目前支持 Memory、MySQL、Cassandra、ElasticSearch 等数据库。

  • Memory :默认存储器,用于简单演示为主,生产环境下不推荐。
  • MySQL :小规模使用时,可以考虑 MySQL 进行存储。
  • ElasticSearch :主流的选择,问了一圈朋友,基本采用 ElasticSearch 存储链路数据。
  • Cassandra :在 Twitter 内部被大规模使用,,因为 Cassandra 易跨站,支持灵活的 schema。

可能有胖友会问是否有 HBase 数据库的支持,暂时没有哈,在 ISSUE#2429:HBase storage support 有相关的简单讨论。

③ API

API 负责提供了一个简单的 JSON API 查询和获取追踪数据。API 的主要消费者就是 Web UI。

④ UI

Web UI 负责提供了基于服务、时间和标记(annotation)查看服务延迟、调用链路、系统依赖等等的界面。

1.2 流程图

让我们来一起看个 Zipkin 的示例流程图:

┌─────────────┐ ┌───────────────────────┐  ┌─────────────┐  ┌──────────────────┐
│ User Code │ │ Trace Instrumentation │ │ Http Client │ │ Zipkin Collector │
└─────────────┘ └───────────────────────┘ └─────────────┘ └──────────────────┘
│ │ │ │
┌─────────┐
│ ──┤GET /foo ├─▶ │ ────┐ │ │
└─────────┘ │ record tags
│ │ ◀───┘ │ │
────┐
│ │ │ add trace headers │ │
◀───┘
│ │ ────┐ │ │
│ record timestamp
│ │ ◀───┘ │ │
┌─────────────────┐
│ │ ──┤GET /foo ├─▶ │ │
│X-B3-TraceId: aa │ ────┐
│ │ │X-B3-SpanId: 6b │ │ │ │
└─────────────────┘ │ invoke
│ │ │ │ request │

│ │ │ │ │
┌────────┐ ◀───┘
│ │ ◀─────┤200 OK ├─────── │ │
────┐ └────────┘
│ │ │ record duration │ │
┌────────┐ ◀───┘
│ ◀──┤200 OK ├── │ │ │
└────────┘ ┌────────────────────────────────┐
│ │ ──┤ asynchronously report span ├────▶ │
│ │
│{ │
│ "traceId": "aa", │
│ "id": "6b", │
│ "name": "get", │
│ "timestamp": 1483945573944000,│
│ "duration": 386000, │
│ "annotations": [ │
│--snip-- │
└────────────────────────────────┘

由上图可以看出,应用的代码(User Code)发起 Http Get 请求(请求路径 /foo),经过 Zipkin Tracer 框架(Trace Instrumentation)拦截,并依次经过如下步骤,记录链路信息到 Zipkin Server 中:

  • 1、record tags :记录 tags 信息到 Span 中。
  • 2、add trace headers :将当前调用链的链路信息记录到 Http Headers 中。
  • 3、record timestamp :记录当前调用的时间戳(timestamp)。
  • 4、发送 HTTP 请求,并携带链路相关的 Header。例如说, X-B3-TraceId:aaX-B3-SpandId:6b
  • 5、调用结束后,记录当次调用所花的时间(duration)。
  • 6、将步骤 1-5,汇总成一个 Span(最小的 Trace 单元),异步上报该 Span 信息给 Zipkin Collector。

1.3 基本概念

Zipkin 有几个重要的基本概念,需要去理解。当然,刚看可能比较会懵逼,可以先指到有这么几个概念,后面慢慢理顺。

① Span

Span :基本工作单元,一次链路调用(可以是 RPC,DB 等等,没有特定的限制)创建一个 Span,通过一个 64 位 ID 标识它。

Span 还有其它的数据,例如描述信息,时间戳,key-value 对的(Annotation)Tag 信息,parent-id 等等。其中,parent-id 可以表示 Span 调用链路来源。

通俗的理解 ,Span 就是一次请求信息。

② Trace

Trace :类似于树结构的 Span 集合,表示一条完整的调用链路,存在唯一标识,即 TraceId 链路编号。

③ Annotation

Annotation :注解,用来记录请求特定事件相关信息(例如时间),通常包含四个注解信息:

  • cs - Client Start,表示客户端发起请求的时间。
  • sr - Server Receive,表示服务端收到请求的时间。
  • ss - Server Send,表示服务端完成处理,并将结果响应给客户端的时间。
  • cr - Client Received,表示客户端接收到服务端返回信息的时间。

BinaryAnnotation :提供一些额外信息,一般以 key-value 对出现。

因为 Zipkin 的模型基本符合 Opentracing 规范,所以后续胖友可以阅读《OpenTracing 官方标准 —— 中文版》文章,进一步了解分布式链路追踪。

2. 搭建 Zipkin 单机环境

考虑到让胖友更快的入门,我们来搭建一个 Zipkin 单机环境,步骤如下:

Zipkin 单机环境

  • 第一步,搭建一个 Elasticsearch 服务。
  • 第二步,搭建一个 Zipkin Server 服务。
  • 第三步,启动两个 Spring Boot 应用,分别作为客户端和服务端。同时配置 Brave 采集链路数据,并通过 HTTP 上传该链路数据给 Zip Server 服务。

仅仅三步,按照艿艿标题党的性格,应该给本文取个《10 分钟快速搭建 Zipkin 服务》标题才对,哈哈哈。

2.1 Elasticsearch 搭建

FROM https://www.elastic.co/cn/products/elasticsearch

Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎,能够解决不断涌现出的各种用例。 作为 Elastic Stack 的核心,它集中存储您的数据,帮助您发现意料之中以及意料之外的情况。

参考《Elasticsearch 极简入门》「1. 单机部署」小节,搭建一个 Elasticsearch 单机服务。

不过要注意,本文使用的是 Elasticsearch 6.5.0 版本。

2.2 Zipkin Server 搭建

在官网 https://zipkin.io/pages/quickstart.html 提供了三种安装方式,我们这里采用官方已经构建好的 jar 包。

① 下载 jar 包

# 创建目录
$ mkdir -p /Users/yunai/zipkin
$ cd /Users/yunai/zipkin

# 下载
$ curl -sSL https://zipkin.io/quickstart.sh | bash -s

这里,我们自动下载了最新版本 2.19.2

② 启动 Zipkin Server

执行 nohup java -jar zipkin.jar --STORAGE_TYPE=elasticsearch --DES_HOSTS=http://127.0.0.1:9200 & 命令,启动 Zipkin Server 并后台运行。其中:

  • 设置 --STORAGE_TYPE=elasticsearch 参数,表示使用 Elasticsearch 存储器。
  • 设置 --DES_HOSTS=http://127.0.0.1:9200 参数,表示使用 http://127.0.0.1:9200 地址的 Elasticsearch。

查看 nohup.out 日志文件,如果文末出现如下日志,说明启动成功:

2020-01-06 23:43:03.067  INFO 34997 --- [oss-http-*:9411] c.l.a.s.Server                           : Serving HTTP at /0:0:0:0:0:0:0:0:9411 - http://127.0.0.1:9411/
2020-01-06 23:43:03.067 INFO 34997 --- [ main] c.l.a.s.ArmeriaAutoConfiguration : Armeria server started at ports: {/0:0:0:0:0:0:0:0:9411=ServerPort(/0:0:0:0:0:0:0:0:9411, [http])}
2020-01-06 23:43:03.088 INFO 34997 --- [ main] z.s.ZipkinServer : Started ZipkinServer in 2.04 seconds (JVM running for 3.741)

  • 默认情况下,Zipkin Server 在 9411 端口,提供服务。

③ 简单使用

使用浏览器,访问 http://127.0.0.1:9411/ 地址,查看 Zipkin UI 界面。如下图所示:Zipkin UI 界面

2.3 zipkin-dependencies 搭建

在使用 Elasticsearch、Cassandra、MySQL 作为存储器后,需要使用 zipkin-dependencies,计算服务之间的依赖关系。不然,我们在 Zipkin UI 的「依赖」菜单,是看不到服务之间的依赖关系图的噢。

①下载 jar 包:

# 创建目录
$ mkdir -p /Users/yunai/zipkin
$ cd /Users/yunai/zipkin

# 下载
$ curl -sSL https://zipkin.io/quickstart.sh | bash -s io.zipkin.dependencies:zipkin-dependencies:LATEST zipkin-dependencies.jar

  • 可能下载会比较慢,请耐心等待,嘿嘿~

② 执行 jar 包:

执行 STORAGE_TYPE=elasticsearch ES_HOSTS=127.0.0.1:9200 java -jar zipkin-dependencies.jar 命令,执行一次 zipkin-dependencies 任务,进行一次服务之间的依赖关系的计算。输出结尾日志如下,代表执行成功:

20/01/07 00:12:40 INFO ElasticsearchDependenciesJob: Processing spans from zipkin-span-2020-01-06
20/01/07 00:12:40 INFO ElasticsearchDependenciesJob: No dependency links could be processed from spans in index zipkin-span-2020-01-06
20/01/07 00:12:40 INFO ElasticsearchDependenciesJob: Done

因为 zipkin-dependencies.jar 实际是个 Spark Job,所以每次任务执行完后,JVM 进程就结束了。需要我们参考 Running in a Spark cluster 文档,将 zipkin-dependencies.jar 部署到 Spark 集群中执行。

不过考虑到可能我们没有 Spark 服务,所以我们也可以考虑使用 Linux 自带的 crontab 定时任务,配置如下,每分钟执行一次:

* * * * * STORAGE_TYPE=elasticsearch ES_HOSTS=127.0.0.1:9200 java -jar /Users/yunai/zipkin/zipkin-dependencies.jar

当然,也可以参考 zipkin-dependencies 代码,修改成使用 Spring Boot 定时任务来进行执行。😈 虽然麻烦一点,但是不用每次重启启动 JVM 进程。

2.4 Spring Boot 应用搭建

考虑到方便胖友,艿艿这里提供了一个最简的 Spring Boot 应用 lab-40-demo-2.2.2.RELEASE.jar。对应 Github 仓库是 lab-40-demo

① 启动 Spring Boot 应用(服务端)

执行 java -jar lab-40-demo-2.2.2.RELEASE.jar --spring.application.name=demo-application-server --server.port=8079 命令,启动 Spring Boot 应用(服务端)。

② 启动 Spring Boot 应用(客户端)

执行 java -jar lab-40-demo-2.2.2.RELEASE.jar --spring.application.name=demo-application-client --server.port=8080 命令,启动 Spring Boot 应用(客户端)。

③ 调用接口

😈 通过不同的启动参数,即使是同一个 jar ,我们也启动了两个 Spring Boot 应用,分别作为客户端和服务端。

下面,我们使用 curl http://127.0.0.1:8080/demo/http 命令,调用下测试接口。该接口,客户端会调用一次服务端。同时,这整个过程会被 Brave 收集链路数据,并上传给 Zipkin Server。

④ 链路数据

调用完该接口之后,使用浏览器,访问 http://127.0.0.1:9411 地址,查看链路数据。点击「查找」按钮,便可看到刚才我们调用接口的链路数据。如下图所示:Zipkin UI —— 链路数据列表

之后,我们点击该链路数据,可以看到一个 Trace 明细。如下图所示:Zipkin UI —— 链路数据明细

再之后,点击第一个 Span,可以看到一个 Span 明细。如下图所示:Zipkin UI —— Span 数据明细

⑤ 依赖数据

点击「依赖」菜单,我们并不能看到和 demo-application-clientdemo-application-server 两者的依赖关系图。

😈 此时,我们可以手动执行下 STORAGE_TYPE=elasticsearch ES_HOSTS=127.0.0.1:9200 java -jar /Users/yunai/zipkin/zipkin-dependencies.jar 命令,执行一次 zipkin-dependencies 任务,进行一次服务之间的依赖关系的计算。

执行完毕之后,刷新浏览器。Amazing 我们看到了 demo-application-clientdemo-application-server 两者的依赖关系图。如下图所示:Zipkin UI —— 依赖数据

⑥ 查看 Elasticsearch 数据

如果胖友有安装 ElasticSearch Head 插件,可以很方便的看到 Zipkin 存储在 Elasticsearch 中的数据。如下图所示:Zipkin UI —— 依赖数据

  • zipkin:span-${yyyy-MM-dd} 索引:Zipkin Span 链路数据,按照天切分数据。
  • zipkin:dependency-${yyyy-MM-dd} 索引:Zipkin 服务之间依赖数据,按照天切分数据。该索引数据,就是通过 zipkin-dependencies 任务,计算获得噢,嘿嘿。

3. 搭建 Zipkin 集群环境

在生产环境下,我们一般推荐搭建 Zipkin 集群环境。😈 当然,如果公司比较抠门,也可以在生产环境下使用 Zipkin 单机环境,毕竟 Zipkin 挂了之后,不影响业务的正常运行。

搭建一个 Zipkin 集群环境,步骤如下:

  • 第一步,搭建一个 Elasticsearch 服务的集群
  • 第二步,搭建一个 Zipkin Server 服务的集群,同时使用 Nginx 进行负载均衡。
  • 第三步,启动一个 Spring Boot 应用。如果采用 HTTP 上传链路数据,Zipkin Server 地址,使用 Nginx 负载均衡后的地址。

😈 具体的搭建过程,并不复杂,胖友自己去尝试下。

4. 告警

Zipkin 并未内置提供告警功能,所以我们需要基于查询 Elasticsearch、MySQL 等存储器的链路数据,自己开发告警功能。

基于 Elasticsearch 的告警方案,我们在《芋道 ELK(Elasticsearch + Logstash + Kibana) 极简入门》「7. 告警」小节,做过简单的介绍。具体的实现,需要胖友先对 Zipkin 存储到 Elasticsearch 的 zipkin:span-${yyyy-MM-dd} 索引的具体每个字段,都有相应的了解。

5. Spring Boot 使用示例

《芋道 Spring Boot 链路追踪 Zipkin 入门》 中,我们来详细学习如何在 Spring Boot 中,整合并使用 Zipkin 收集链路数据。😈 相比「2.4 Spring Boot 应用搭建」来说,我们会提供更加丰富的示例哟。

6. Spring Cloud 使用示例

《芋道 Spring Cloud 链路追踪 Spring Cloud Sleuth》 中,我们来详细学习如何在 Spring Cloud 中,整合并使用 Zipkin 收集链路数据。😈 相比「2.4 Spring Boot 应用搭建」来说,我们会提供更加丰富的示例哟。

666. 彩蛋

本文仅仅是简单的 Zipkin 入门文章,如果胖友想要更好的使用 Zipkin,推荐通读下《Zipkin 中文文档》

想要进一步深入的胖友,也可以阅读如下资料:

😈 最后弱弱的问一句,上完 Zipkin 之后,有没发现自己系统各种地方慢慢慢!嘻嘻。

文章目录
  1. 1. 1. 概述
    1. 1.1. 1.1 整体架构
      1. 1.1.1. 1.1.1 Zipkin Tracer
      2. 1.1.2. 1.1.2 Transport
      3. 1.1.3. 1.1.3 Zipkin Server
    2. 1.2. 1.2 流程图
    3. 1.3. 1.3 基本概念
  2. 2. 2. 搭建 Zipkin 单机环境
    1. 2.1. 2.1 Elasticsearch 搭建
    2. 2.2. 2.2 Zipkin Server 搭建
    3. 2.3. 2.3 zipkin-dependencies 搭建
    4. 2.4. 2.4 Spring Boot 应用搭建
  3. 3. 3. 搭建 Zipkin 集群环境
  4. 4. 4. 告警
  5. 5. 5. Spring Boot 使用示例
  6. 6. 6. Spring Cloud 使用示例
  7. 7. 666. 彩蛋