12-第11课:服务链路追踪

  1. Zipkin 简介
  2. SpringCloudSleuth 简介
    1. Sleuth 术语
  3. 简单的链路追踪实现
  4. 通过消息中间件实现链路追踪
  5. 存储追踪数据

在前面的课程中,我们已经学习了使用 Actuator 监控微服务,使用 Hystrix 监控 Hystrix Command。本文,我们来研究微服务链路追踪。

我们知道,微服务之间通过网络进行通信。在我们提供服务的同时,我们不能保证网络一定是畅通的,相反,网络是很脆弱的,网络资源也有限。因此,我们有必要追踪每个网络请求,了解其经过了哪些微服务,延迟多少,每个请求所耗费的时间等。只有这样,我们才能更好的分析系统拼劲,解决系统问题。

本课,我们主要探讨服务追踪组件 Zipkin,SpringCloudSleuth 集成了 Zipkin。

Zipkin 简介

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

SpringCloudSleuth 简介

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

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 时间戳

下图演示了请求依次经过 SERVICE1 -> SERVICE2 -> SERVICE3 -> SERVICE4 时,span、trace、annotation 的变化:

enter image description here

简单的链路追踪实现

要实现完整的服务器链路,需要分为服务端和客户端,下面我们来分别介绍服务端和客户端的实现: 在 Spring Boot 2.0 以前,我们需要自己实现 Zipkin 服务端,从 Spring Boot 2.0 以后,其推出了官方 Zipkin 服务端,我们只需要下载服务端 jar 包,放到服务器上,启动即可。具体操作如下:

(1)从网络上下载 Zipkin 服务端的可执行 jar 包,下载地址可点击这里获取

(2)将 zipkin-server-2.9.4-exec.jar 修改为 zipkin.jar;

(3)命令行终端进入 zipkin.jar 所在目录,执行命令:java -jar zipkin.jar。

启动成功后,如图所示:

Zipkin 服务端的默认启动端口为 9411,浏览器访问 http://localhost:9411 即可进入 Zipkin 服务端管理界面,如图:

enter image description here

单纯集成 zipkinServer 还达不到追踪的目的,还必须使我们的微服务客户端集成 Zipkin 才能跟踪微服务,下面是集成步骤。

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

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

(2)在 Git 仓库的配置文件 eurekaclient.yml 中添加以下内容:

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

其中,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 列表:

enter image description here

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

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

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

(1)命令行启动官网提供的 zipkin.jar,注意,启动时需要指定 RabbitMQ 的 host 地址,如:

java -jar zipkin.jar --RABBIT_ADDRESSES=127.0.0.1

其中,–RABBIT_ADDRESSES 即为 RabbitMQ 的 host 地址。

(2)启动完成后,我们访问 RabbitMQ 的 Web 管理界面,可以看到 Zipkin Server 已经为我们创建了一个名叫 zipkin 的队列,如图:

enter image description here

(3)改造 EurekaClient,将 pom.xml 添加如下内容:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>

(4)Git 仓库的配置文件 EurekaClient 去掉 spring.zipkin.base-url 配置。

(5)依次启动相应工程,我们发现依然可以正常跟踪微服务。

存储追踪数据

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

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

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

(1)首先创建一个名为 zipkin_db 的数据库,并执行以下脚本:

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.jar,这次启动需要指定数据库连接信息,如:

java -jar zipkin.jar –RABBIT_ADDRESSES=127.0.0.1 –MYSQL_HOST=127.0.0.1 –MYSQL_TCP_PORT=3306 –MYSQL_USER=root –MYSQL_PASS=1qaz2wsx –MYSQL_DB=zipkin_db –STORAGE_TYPE=mysql

注:如果启动失败,可能的原因有:

  • 数据库无法连接

  • MySQL 版本过高(大于等于 8.0),请降低版本,如果是 MariaDB,则最好安装其官网最新版本。

  • 重启工程,可以看到数据库已经存储了追踪数据,如图:

    enter image description here

且重启 Zipkin Server 后,也能通过 http://localhost:9411 查询到追踪数据。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 tuyrk@qq.com

文章标题:12-第11课:服务链路追踪

文章字数:2.1k

本文作者:神秘的小岛岛

发布时间:2020-07-03, 11:59:32

最后更新:2020-07-14, 23:05:02

原始链接:https://www.tuyrk.cn/gitchat/springcloud-quickly/12-springcloud-sleuth/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏