前言

前面一篇博文我们讲了Spring Cloud的一个组件,实际上通过一些组件的结合我们已经可以搭建一个基础的微服务架构,后续还有一些组件会陆续更新的   然而,在实际应用中,随着业务的发展,微服务的数量会越来越多,服务之间的调用关系也会越来越复杂,一个请求可能会经过多个微服务的协作调用来获取结果,这时候,全局的服务链路追踪显得非常必要,一旦一个请求出现失败的回调,我们可以立马通过服务追踪来寻找错误根源并加以修改。

Spring Cloud Sleuth 主要功能就是在分布式系统中提供追踪解决方案 ,并且兼容了zipkin,提供了REST API接口来辅助我们查询跟踪数据以实现对分布式系统的监控程序 。

Spring Cloud Sleuth 简介

下面这段文字是从官网翻译过来的,有兴趣可以到官网查看原文 http://cloud.spring.io/spring-cloud-static/Finchley.RELEASE/single/spring-cloud.html

Spring Cloud Sleuth 借用了 Dapper 的术语

Span:基本工作单元,例如,在一个新建的span中发送一个RPC等同于发送一个回应请求给RPC,span通过一个64位ID唯一标识,trace以另一个64位ID表示,span还有其他数据信息,比如摘要、时间戳事件、关键值注释(tags)、span的ID、以及进度ID(通常是IP地址) 。

span在不断的启动和停止,同时记录了时间信息,当你创建了一个span,你必须在未来的某个时刻停止它。

最初的 span 是从一个叫 root span 开启链路的,span 中 ID的值等于 追踪的ID。

Trace:一系列spans组成的一个树状结构,例如,如果你正在跑一个分布式大数据工程,你可能需要创建一个trace。

Annotation:用来及时记录一个事件的存在,一些核心annotations用来定义一个请求的开始和结束

cs - Client Sent -客户端发起一个请求,这个annotion描述了这个span的开始
sr - Server Received -服务端获得请求并准备开始处理它,如果将其sr减去cs时间戳便可得到网络延迟
ss - Server Sent -注解表明请求处理的完成(当请求返回客户端),如果ss减去sr时间戳便可得到服务端需要的处理请求时间
cr - Client Received -表明span的结束,客户端成功接收到服务端的回复,如果cr减去cs时间戳便可得到客户端从服务端获取回复的所有所需时间
下面的图片显示 Span和Trace配合Zipkin注解 在系统中的查看情形:
https://img-blog.csdn.net/20180806211105732?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lleWF6aGlzaGFuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70
每一个标注的颜色表示一个span,(有七个span,从A到G),例如下面的标注:

Trace Id = X
Span Id = D
Client Sent
这些语句表明了当前的span 有值设为X的 Trace Id 和 值为 D 的 Span Id

下面的图片显示了span 视图中的父子关系:
https://img-blog.csdn.net/20180806211140250?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lleWF6aGlzaGFuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70
以上就是对于这个组件的一些术语信息的简单介绍。

准备工程

前面讲到,Spring Cloud Sleuth 兼容了Zipkin。Zipkin是Twitter的一个开源项目,它基于Google Dapper实现。我们可以使用它来收集各个服务器上请求链路的跟踪数据,并通过它提供的REST API接口来辅助我们查询跟踪数据以实现对分布式系统的监控程序 。

所以,在本章的案例中,我们需要有三个工程,一个是提供收集调用数据的zipkin-server,还有两个对外暴露接口的工程trace-A和trace-B,两个工程都向外暴露接口可供其他服务调用,一旦调用成功,zipkin-server就可以记录服务调用的链路追踪数据。

首先需要准备zipkin-server工程,Spring Cloud官方提供了一系列关于zipkin-server工程的jar,开发者直接下载并启动即可使用,笔者使用的 zipkin-server-2.10.4-exec.jar 的jar 包,下载地址为https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/2.10.4/zipkin-server-2.10.4-exec.jar

运行ctrl + R ,输入cmd,打开命令行窗口跳转到jar所在的文件路径下,运行

java -jar zipkin-server-2.10.1-exec.jar
开启成功后可以看到工程的端口为 9411.
https://img-blog.csdn.net/20180806211244455?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lleWF6aGlzaGFuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70
打开浏览器访问 http://localhost:9411,会出现这样的界面
https://img-blog.csdn.net/20180806211254488?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lleWF6aGlzaGFuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70
这是zipkin-server中集成的ui界面,点击 “查找调用链 ” 可以查看服务的追踪链路关系。因为现在我们没有启动服务在互相调用,所以这里看不到链路。

新建两个服务工程
新建一个module工程,命名为trace-A,在pom文件中加入web的基础依赖以及spring-cloud-start-zipkin依赖 ,具体代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">;
<modelVersion>4.0.0</modelVersion>

<groupId>com.yeya</groupId>
<artifactId>zipkin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>trace-A</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

  <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-test</artifactId>
     <scope>test</scope>
  </dependency>

</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
接着,配置application.yml,

server:
port: 8886
spring:
zipkin:
base-url: http://localhost:9411
application:
name: trace-A
其中,base-url代表的是zipkin-server工程的地址,这样就能被zipkin-server所追踪。
配置好之后,写好两个接口,分别是 返回调用其他服务的结果的接口 “hello” 以及输出本服务信息的接口 “info”,
代码如下:

@SpringBootApplicationbr/>@RestController
public class TraceAApplication {

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

@Autowired
private RestTemplate restTemplate;

@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}

@RequestMapping("/hello")
public String toTraceA(){
return restTemplate.getForObject("
http://localhost:8887/info", String.class);
}

@RequestMapping("/info")
public String info(){
return "trace A";

}
}
接着创建新的module工程trace-B,创建过程同trace-A,引入相同的依赖并添加配置,在应用启动类中同样加两个接口 “hi” 和 “info”,

@RequestMapping("/hi")
public String toTraceB(){
return restTemplate.getForObject("http://localhost:8886/info", String.class);
}

@RequestMapping("/info")
public String info(){
return "trace B";

}
添加代码后,依次启动两个工程,在浏览器访问 http://localhost:8886/hello,窗口会显示

trace B
同样,访问 http://localhost:8887/hi ,会显示
trace A
这是,我们重新访问 http://localhost:9411 ,点击 “依赖分析” 可以看到 服务的调用链路关系
https://img-blog.csdn.net/20180806211340731?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lleWF6aGlzaGFuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70
访问首页的 “find traces” 可以查看服务间调用的具体数据
https://img-blog.csdn.net/20180806211357711?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lleWF6aGlzaGFuZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70

总结:

到这里,我们的服务链路追踪就实现了,是不是觉得很简单,其实是zipkin帮我们做了很多事,有兴趣的读者可以通过debug来跟踪源码查看,这样对于组件的运行过程会有比较好的了解。