写在前面

虽然通过ELK日志分析平台提供的收集、存储、搜索等功能,我们已经非常轻松的实现对跟踪信息的管理,但是在ELK平台中缺乏对请求链路中各阶段时间延迟的关注,很多时候我们追溯请求链路的一个原因,就是为了找出整个调用链路中出现延迟过高的瓶颈,或者为了实现对分布式系统做延迟监控等与时间消耗相关的需求,也就是说此时的ELK平台是无法满足我们的要求,应当引入Zipkin框架来解决。

Zipkin

Zipkin是Twitter的一个开源项目,它基于Google Dapper实现。开发者可以使用它来收集各个服务器上请求链路的跟踪数据,并通过它提供的RESTful API接口来辅助查询跟踪数据,以实现对分布式系统的监控,从而及时发现系统中出现的延迟升高问题,并找出系统性能瓶颈的源头。Zipkin除了提供面向开发的API接口外,它还提供了方便的UI组件来帮助我们直观地跟踪信息和分析请求链路明细,如可以查询某段时间内各用户请求的处理时间。

Zipkin基本概念

(1)Span:它是Zipkin的基本工作单元,一次链路调用就会创建一个Span。

(2)Trace:它是一组Span的集合,表示一条调用链路。举个例子来说,假设当前服务A调用服务B然后调用服务C,那么此时A->B->C的链路就是一条Trace,而每个服务如A就是一个Span,如果在服务B中另起2个线程分别调用了服务D和服务E,那么服务D和服务E就是服务B的子Span。

事实上这些概念对应于Spring Cloud Sleuth组件中Span ID和Trace ID,一条请求链路中包含一个Trace ID,多个Span ID。

Zipkin基础架构

下图是Zipkin的基础架构,从图中可以看出它由4个核心组件:Collector、Storage、RESTful API、WebUI组成:

Collector是收集器组件,它主要处理从外部系统发送过来的跟踪信息,并将这些信息转换为Zipkin内部处理的Span格式,以支持后续的存储、分析和展示等功能。

Storage是存储组件,它主要处理收集器接收到的跟踪信息,默认会将这些信息存储在内存中。开发者也可以修改此默认的存储策略,使用其他的存储组件来将跟踪信息存储在数据库中。

RESTful是API组件,它主要用于提供外部访问的接口,如给客户端展示跟踪信息,或者外接系统访问以实现监控等。

WebUI是UI组件,顾名思义就是基于API组件而实现的上层应用,通过UI组件,用户可以直观地查询和分析跟踪信息。

HTTP收集

Spring Cloud Sleuth对整合Zipkin提供了自动化配置的封装,因此开发者可以很方便的使用Zipkin。接下来学习Sleuth与Zipkin的基础整合过程,主要分为以下两部分:

第一部分,搭建Zipkin Server。请注意SpringBoot自 2.x.x版本开始已经不推荐开发者自定义zipkin-server,官方推荐的是在官网下载jar包,直接启动zipkin-server即可。笔者这里选择 2.12.6 的版本,然后选择jar包进行下载:

之后进入DOS状态,cd到该jar包所在目录,然后执行如下命令来启动zipkin-server:

1
start /min java -jar zipkin-server-2.12.6-exec.jar

注意此时使用的各项配置均是默认的配置,之后打开浏览器,去访问http://localhost:9411/zipkin/链接,即可出现如下所示的Zipkin管理页面:

第二部分,为应用引入和配置Zipkin服务。接下来我们需要对之前的trace-one和trace-two项目进行一些配置,以实现将信息输出到Zipkin Server中。

第一步,在trace-one和trace-two项目的pom.xml依赖文件中引入spring-cloud-starter-zipkin依赖(注意因为前面已经引入了spring-cloud-starter-sleuth,如果没有则需要引入):

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

第二步,在在trace-one和trace-two项目的application.properties配置文件中增加Zipkin Server的配置信息,其实这个可以不添加,因为官方默认的配置信息就是localhost和9411端口,如下所示:

1
2
3
4
5
# Zipkin Server配置
spring.zipkin.base-url=http://localhost:9411
spring.zipkin.enabled=true
spring.sleuth.sampler.probability=1
spring.zipkin.sender.type=web

测试分析

在完成上述两个部分的操作后,接下来就是启动服务,必须按照如下顺序来启动服务:eureka-server服务注册中心、trace-one、trace-two。之后在浏览器地址栏中访问http://localhost:5001/trace-one,当在开发者在控制台发现跟踪信息的最后一个值为true的时候,就说明该跟踪信息会输出到Zipkin Server中,这一点在前面已经进行了说明:

此时就可以在Zipkin Server的管理页面中选择合适的查询条件,如traceID就可以查询出刚才在日志中出现的跟踪信息,如下所示:

点击下方的trace-one端点的跟踪信息,还可以得到Sleuth跟踪到的详细信息,其中就包括我们之前关注的请求时间消耗:

单击导航栏中的依赖菜单,还可以查看Zipkin Server根据跟踪信息分析生成的系统请求链路依赖关系图:

消息中间件收集

Spring Cloud Sleuth在整合Zipkin时不仅实现了以HTTP方式来收集跟踪信息,还可以通过使用消息中间件来对跟踪信息进行异步收集的封装。为什么提供多种方式呢?假设当应用与zipkin服务端网络不通或者闪断的情况下,采用HTTP方式在此种情况是无法正常收集,且zipkin默认是将数据存储在内存中,如果服务端重启或者宕机,数据将会丢失,因此还提供了消息中间件的方式。

通过结合Spring Cloud Stream,我们可以非常轻松的让应用客户端将跟踪信息输出到消息中间件上,同时zipkin服务端从消息中间件上通过异步方式来消费这些跟踪信息。

接下来我们基于之前实现的trace-one和trace-two项目以及官方提供的zipkin-server做一些改变,以实现通过消息中间件来收集跟踪信息,且改造的内容非常简单,只需修改项目依赖和部分配置文件即可。

改造客户端

为了让trace-one和trace-two项目在产生跟踪信息后,能够将抽样记录输出到消息中间件,除了需要之前引入的spring-cloud-starter-sleuth依赖外,还需要引入rabbitmq依赖spring-rabbit,如下所示:

1
2
3
4
 <dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>

请注意不同版本使用的依赖不尽相同,建议使用 官方 提示的依赖,这样才不会出问题。

接着回到两个项目的application.properties配置文件中,去掉其中HTTP方式实现时使用的spring.zipkin.base-url参数,并根据实际情况增加消息中间件的相关配置,这里使用RabbitMQ这一消息中间件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Zipkin Server配置
spring.sleuth.sampler.probability=1
spring.sleuth.web.enabled=true

# HTTP方式
#spring.zipkin.base-url=http://localhost:9411
#spring.zipkin.enabled=true

# 消息中间件方式
spring.zipkin.sender.type=rabbit
# RabbitMQ相关设置
# 主机
spring.rabbitmq.host=localhost
# 端口
spring.rabbitmq.port=5672
# 用户名
spring.rabbitmq.username=springcloud
# 密码
spring.rabbitmq.password=springcloud

改造服务端

由于我们使用的是官方推荐的zipkin-server,且已经打包成了jar包,因此直接修改其代码是不现实的,但是通常而言我们只是对于一些配置信息会发生修改,而对于项目依赖jar包几乎不会修改,因此可以通过在启动的时候设置参数来达到自定义配置这一目的。下面是rabbit相关的一些参数,如下表所示,这些都是可以配置的参数:

属性 环境变量 描述
zipkin.collector.rabbitmq.concurrency RABBIT_CONCURRENCY 并发消费者数量,默认为1
zipkin.collector.rabbitmq.connection-timeout RABBIT_CONNECTION_TIMEOUT 建立连接时的超时时间,默认为60000毫秒,即1分钟
zipkin.collector.rabbitmq.queue RABBIT_QUEUE 从中获取span信息的队列,默认为zipkin
zipkin.zipkin.collector.rabbitmq.uri RABBIT_URI 符合RabbitMQ URI 规范的URI,如amqp://user:pass@host:10000/vhost

请注意如果设置了URI参数,那么下表中的参数将会被忽略,而不生效:

属性 环境变量 描述
zipkin.collector.rabbitmq.addresses RABBIT_ADDRESSES 用逗号分隔的RabbitMQ地址列表,如localhost:5672,localhost:5673
zipkin.collector.rabbitmq.password RABBIT_PASSWORD 连接到RabbitMQ时使用的密码,默认为guest
zipkin.collector.rabbitmq.username RABBIT_USER 连接到RabbitMQ时使用的用户名,默认为guest
zipkin.collector.rabbitmq.virtual-host RABBIT_VIRTUAL_HOST 使用的RabbitMQ virtual host,默认为/
zipkin.collector.rabbitmq.use-ssl RABBIT_USE_SSL 设置为true时使用SSL的方式与RabbitMQ建立链接

一般而言,使用RabbitMQ都需要指定四个参数:IP地址、端口号、用户名、密码。这里上面介绍了两种方式,第一种是通过指定RABBIT_URI的方式,这种方式同时指定了上述四个参数,如amqp://user:pass@host:10000/vhost;第二种则是比较巧,默认情况下端口号、用户名和密码都是有默认值的,即默认端口号为5672,注意Web页面的端口号是15672,用户名是guest,密码也是guest。如果上述三个参数你都没有修改,那么你完全可以在启动的时候只指定RABBIT_ADDRESSES参数。

笔者只是修改了默认的用户名和密码,因此可以使用如下方式来启动zipkin-server项目:

1
start /min java -jar zipkin-server-2.12.6-exec.jar --zipkin.collector.rabbitmq.username=springcloud --zipkin.collector.rabbitmq.password=springcloud

测试分析

在完成上述两个部分的操作后,接下来就是启动服务,必须按照如下顺序来启动服务:eureka-server服务注册中心、trace-one、trace-two和RabbitMQ。之后在浏览器地址栏中访问http://localhost:5001/trace-one,可以看到页面正常显示结果:

接着访问RabbitMQ的管理页面链接http://localhost:15672/,并点击queues按钮,可以看到此时多出了一个Zipkin队列,这样其实就说明zipkin-server就可以接收到RabbitMQ的消息:

这样关于Spring Cloud Sleuth分布式服务跟踪整合Zipkin的学习就到此为止,后续开始学习Zipkin收集跟踪信息的详细过程,了解它们对于理解Sleuth生产跟踪信息以及输出跟踪信息的整体过程和工作原理有非常重要的帮助。