具有关联ID的统一日志记录平台是一个强大的调试工具。但是,在本章的剩余部分中,我们将不再关注如何跟踪日志条目,而是关注如何跨不同微服务可视化事务流。一张干净简洁的图片比一百万条日志条目有用。
分布式跟踪涉及提供一张可视化的图片,说明事务如何流经不同的微服务。分布式跟踪工具还将对单个微服务响应时间作出粗略的估计。但是,分布式跟踪工具不应该与成熟的应用程序性能管理(Application Performance Management,APM)包混淆。这些包可以为服务中的实际代码提供开箱即用的低级性能数据,除了提供响应时间,它还能提供其他性能数据,如内存利用率、CPU利用率和I/O利用率。
这就是Spring Cloud Sleuth和OpenZipkin(也称为Zipkin)项目的亮点。Zipkin是一个分布式跟踪平台,可用于跟踪跨多个服务调用的事务。Zipkin允许开发人员以图形方式查看事务占用的时间量,并分解在调用中涉及的每个微服务所用的时间。在微服务架构中,Zipkin是识别性能问题的宝贵工具。
建立Spring Cloud Sleuth和Zipkin涉及4项操作:
将Spring Cloud Sleuth和Zipkin JAR文件添加到捕获跟踪数据的服务中;
在每个服务中配置Spring属性以指向收集跟踪数据的Zipkin服务器;
安装和配置Zipkin服务器以收集数据;
定义每个客户端所使用的采样策略,便于向Zipkin发送跟踪信息。
9.3.1 添加Spring Cloud Sleuth和Zipkin依赖项
到目前为止,我们已经将两个Maven依赖项包含到Zuul服务、许可证服务以及组织服务中。这些JAR文件是 spring-cloud-starter-sleuth和spring-cloud-sleuth-core依赖项。spring-cloud-starter-sleuth依赖项用于包含在服务中启用Spring Cloud Sleuth所需的基本Spring Cloud Sleuth库。当开发人员必须要以编程方式与Spring Cloud Sleuth进行交互时,就需要使用 spring-cloud-sleuth-core依赖项(本章后面将再次使用它)。
要与Zipkin集成,需要添加第二个Maven依赖项,名为spring-cloud-sleuth-zipkin。代码清单9-3展示了添加spring-cloud-sleuth-zipkin依赖项后,在Zuul、许可证以及组织服务中应该存在的Maven条目。
代码清单9-3 客户端的Spring Cloud Sleuth和Zipkin依赖项
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
9.3.2 配置服务以指向Zipkin
有了JAR文件,接下来就需要配置想要与Zipkin进行通信的每一项服务。这项任务可以通过设置一个Spring属性spring.zipkin.baseUrl来完成,该属性定义了用于与Zipkin通信的URL,它设置在每个服务的application.yml属性文件中。
注意
spring.zipkin.baseUrl也可以作为Spring Cloud Config中的属性进行外部化。
在每个服务的application.yml文件中,将该值设置为http://localhost:9411。但是,在运行时,我使用在每个服务的Docker配置文件(docker/common/docker-compose.yml)上传递的ZIPKIN_URI(http://zipkin:9411)变量来覆盖这个值。
Zipkin、RabbitMQ与Kafka
Zipkin确实有能力通过RabbitMQ或Kafka将其跟踪数据发送到Zipkin服务器。从功能的角度来看,不管使用HTTP、RabbitMQ还是Kafka,Zipkin的行为没有任何差异。通过使用HTTP跟踪,Zipkin使用异步线程发送性能数据。另外,使用RabbitMQ或Kafka来收集跟踪数据的主要优势是,如果Zipkin服务器关闭,任何发送给Zipkin的跟踪信息都将“排队”,直到Zipkin能够收集到数据。
Spring Cloud Sleuth通过RabbitMQ和Kafka向Zipkin发送数据的配置在Spring Cloud Sleuth文档中有介绍,因此本章将不再赘述。
9.3.3 安装和配置Zipkin服务器
要使用Zipkin,首先需要按照本书多次所做的那样建立一个Spring Boot项目(本章的项目名为 zipkinsvr)。接下来,需要向zipkinsvr/pom.xml文件添加两个JAR依赖项。代码清单9-4展示了这两个JAR依赖项。
代码清单9-4 Zipkin服务所需的JAR依赖项
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId> ⇽--- 这个依赖项包含用于创建Zipkin服务器所需的核心类
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId> ⇽--- 这个依赖项包含用于运行Zipkin服务器的UI部分所需的核心类 </dependency>
选择@EnableZipkinServer还是@EnableZipkinStreamServer
关于上述JAR依赖项,有一件事需要注意,那就是它们不是基于Spring Cloud的依赖项。虽然Zipkin是一个基于Spring Boot的项目,但是@EnableZipkinServer并不是一个Spring Cloud注解,它是Zipkin项目的一部分。这通常会让Spring Cloud Sleuth和Zipkin的新手混淆,因为Spring Cloud团队确实编写了@EnableZipkinStreamServer注解作为Spring Cloud Sleuth的一部分,它简化了Zipkin与RabbitMQ和Kafka的使用。
我选择使用@EnableZipkinServer是因为对本章来说它创建简单。使用@EnableZipkinStreamServer需要创建和配置正在跟踪的服务以发布消息到RabbitMQ或Kafka,此外,还需要设置和配置Zipkin服务器来监听RabbitMQ或Kafka,以此来跟踪数据。@EnableZipkinStreamServer注解的优点是,即使Zipkin服务器不可用,也可以继续收集跟踪数据。这是因为跟踪消息将在消息队列中累积跟踪数据,直到Zipkin服务器可用于处理消息记录。如果使用了@EnableZipkinServer注解,而Zipkin服务器不可用,那么服务发送给Zipkin的跟踪数据将会丢失。
在定义完JAR依赖项之后,现在需要将@EnableZipkinServer注解添加到Zipkin服务引导类中。这个类位于zipkinsvr/src/main/java/com/thoughtmechanix/zipkinsvr/ZipkinServerApplication.java中。代码清单9-5展示了引导类的代码。
代码清单9-5 构建Zipkin服务器引导类
package com.thoughtmechanix.zipkinsvr;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import zipkin.server.EnableZipkinServer;
⇽--- @EnableZipkinServer 允许快速启动Zipkin作为Spring Boot项目
@SpringBootApplication
@EnableZipkinServer
public class ZipkinServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinServerApplication.class, args);
}
}
在代码清单9-5中要注意的关键点是@EnableZipkinServer注解的使用。这个注解能够启动这个Spring Boot服务作为一个Zipkin服务器。此时,读者可以构建、编译和启动Zipkin服务器,作为本章的Docker容器之一。
运行Zipkin服务器只需要很少的配置。在运行Zipkin服务器时,唯一需要配置的东西,就是Zipkin存储来自服务的跟踪数据的后端数据存储。Zipkin支持4种不同的后端数据存储。这些数据存储是:
(1)内存数据;
(2)MySQL;
(3)Cassandra;
(4)Elasticsearch。
在默认情况下,Zipkin使用内存数据存储来存储跟踪数据。Zipkin团队建议不要在生产系统中使用内存数据库。内存数据库只能容纳有限的数据,并且在Zipkin服务器关闭或丢失时,数据就会丢失。
注意
对于本书来讲,我们将使用Zipkin的内存数据存储。配置Zipkin中使用的各个数据存储超出了本书的范围,但是,如果读者对这个主题感兴趣,可以在Zipkin GitHub存储库中查阅更多信息。
9.3.4 设置跟踪级别
到目前为止,我们已经配置了要与Zipkin服务器通信的客户端,并且已经配置完Zipkin服务器准备运行。在开始使用Zipkin之前,我们还需要再做一件事情,那就是定义每个服务应该向Zipkin写入数据的频率。
在默认情况下,Zipkin只会将所有事务的10%写入Zipkin服务器。可以通过在每一个向Zipkin发送数据的服务上设置一个Spring属性来控制事务采样。这个属性叫spring.sleuth.sampler.percentage,它的值介于0和1之间。
值为0表示Spring Cloud Sleuth不会发送任何事务数据。
值为0.5表示Spring Cloud Sleuth将发送所有事务的50%。
对于本章来讲,我们将为所有服务发送跟踪信息。要做到这一点,我们可以设置spring.sleuth.sampler.percentage的值,也可以使用AlwaysSampler替换Spring Cloud Sleuth中使用的默认Sampler类。AlwaysSampler可以作为Spring Bean注入应用程序中。例如,许可证服务在licensing-service/src/main/java/com/thoughtmechanix/licenses/Application.java 中将AlwaysSampler定义为Spring Bean。
@Bean
public Sampler defaultSampler() { return new AlwaysSampler();}
Zuul服务、许可证服务和组织服务都定义了AlwaysSampler,因此在本章中,所有的事务都会被Zipkin跟踪。
spring cloud zipkin elk