为什么需要Spring Cloud Sleuth

Spring Cloud Seleuth是SpringCloud的一个组件,它的主要功能是在分布式系统中提供服务链路追踪解决方案。

微服务架构是一个分布式架构,微服务系统按业务划分服务单元,一个微服务系统往往有很多个服务单元。由于服务单元数量众多,业务的复杂性较高,如果出现了错误和异常,很难去定位。主要体现在一个请求可能需要调用很多个服务,而内部服务的调用复杂性决定了问题难以定位。所以在微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与,参与的顺序又是怎样的,从而达到每个请求的步骤清晰可见,出了问题能够快速定位的目的。

Google开源了Dapper 链路追踪组件,并在2010 年发表了论文《Dapper, a Large-ScaleDistributed Systems Tracing Infrastructure)》,这篇论文是业内实现链路追踪的标杆和理论基础,具有很高的参考价值。

目前,常见的链路追踪组件有Google 的Dapper、Twitter 的Zipkin,以及阿里的Eagleeye(鹰眼)等,它们都是非常优秀的链路追踪开源组件。

基本术语

Spring Cloud Sleuth采用了Google的开源项目Dapper的专业术语。

  1. Span:基本工作单元,发送一个远程调度任务就会产生一个Span,Span是用一个64位ID唯一标识的,Trace是用另一个64位ID唯一标识的。Span还包含了其他的信息,例如摘要、时间戳事件、Span的ID以及进行ID。
  2. Trace:由一系列Span组成的,呈树状结构。请求一个微服务系统的API接口,这个API接口需要调用多个微服务单元,调用每个微服务单元都会产生一个新的Span,所有由这个请求产生的Span组成了这个Trace。
  3. Annotation:用于记录一个事件,一些核心注解用于定义一个请求的开始和结束:
  • cs-Client Sent:客户端发送一个请求,这个注解描述了Span的开始;
  • sr-Server Received:服务端获得请求并准备开始处理它,如果将其sr减去cs时间戳,便可得到网络传输的时间;
  • ss-Server Sent:服务端发送响应,该注解表明请求处理的完成(当请求返回客户端),用ss的时间戳减去sr时间戳,便可以得到服务器请求的时间。
  • cr-Client Received:客户端接收响应,此时Span结束,如果cr的时间戳减去cs时间戳,便可以得到请求所消耗的时间;
简单实例

构建Eureka Server

依赖:

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

配置:

server:
  port: 8761

spring:
  application:
    name: eureka-server

eureka:
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
      defaultZone: http://localhost:${server.port}/eureka/

启动类:

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

构建Zipkin Server:
新建一个Module工程,取名为zipkin-server, 工程的pom文件继承了主Maven工程的pom文件,并引入Eureka 的起步依赖spring-cloud-starter-eureka、Zipkin Server 的依赖zipkin-server,以及Zipkin Server 的UI界面依赖zipkin-autoconfigure-ui。后两个依赖提供了Zipkin的功能和Zipkin界面展示的功能,代码如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>io.zipkin.java</groupId>
        <artifactId>zipkin-server</artifactId>
    </dependency>
    <dependency>
        <groupId>io.zipkin.java</groupId>
        <artifactId>zipkin-autoconfigure-ui</artifactId>
    </dependency>
</dependencies>

在程序的启动类ZipkinServiceApplication加上@EnableZipkinServer注解,开启ZipkinServer 的功能,加上@EnableEurekaClient注解,启动Eureka Client,代码如下:

@SpringBootApplication
@EnableEurekaClient
@EnableZipkinServer
public class ZipkinServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZipkinServerApplication.class, args);
    }
}

配置:

server:
  port: 9411

spring:
  application:
    name: zipkin-server

eureka:
  client:
    service-url: 
      defaultZone: http://localhost:8761/eureka/

构建User Service:
在主Maven工程下建一个Module工程,取名为user-service,作为服务提供者,对外暴露API接口。

user-service 工程的pom文件继承了主Maven工程的pom文件,并引入了Eureka的起步依赖spring-cloud-starter-eureka、Web起步依赖spring-boot- starter-web,以及Zipkin的起步起来spring-cloud-starter-zipkin。

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <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>
        <version>RELEASE</version>
    </dependency>
</dependencies>

在程序的配置文件applicatiom.yml中,指定程序名为user-service, 端口号为8762,服务注册地址http://localhost:8761/eurea/,Zipkin Server地址为http://localhost:9411spring.sleuth.sampler.percentage为1.0, 即以100%的概率将链路的数据上传给Zipkin Server, 在默认情况下,该值为0.1。配置文件代码如下:

server:
  port: 8762

spring:
  application:
    name: user-service
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      percentage: 1.0
eureka:
  client:
    service-url: 
      defaultZone: http://localhost:8761/eureka/

在UserController类建一个"/user/hi"的API接口,对外提供服务:

@RestController
@RequestMapping("/user")
public class UserController {
    
    @GetMapping("/hi")
    public String hi() {
        return "I'm forezp";
    }
}

最后作为Eureka Client,需要在程序的启动类UserServiceApplication 加上@EnableEurekaClient注解,开启Eureka Client的功能。

构建Gateway Service:
新建一个名为gateway-service的工程,这个工程作为服务网关,将请求转发到user-service。作为Zipkin客户端,需要将链路数据上传给Zipkin Server,同时它也作为Eureka Client。在工程的pom文件中除了需要继承主Maven工程的pom文件,还需引入如下的依赖,代码清单如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zuul</artifactId>
    </dependency>
    <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>
        <version>RELEASE</version>
    </dependency>
</dependencies>

在工程的配置文件application.yml中,配置程序名为gateway-service, 端口号为5000,服务注册地址为http://locahost:8761/eureka/, Zipkin Server 地址为http://localhost:9411, 以“/user-api/**" 开头的Uri请求转发到服务名为user-service 的服务,配置代码如下:

server:
  port: 5000

spring:
  application:
    name: gateway-service
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      percentage: 1.0

zuul:
  routes: 
    api-a:
      path: /user-api/**
      serviceId: user-service

在程序的启动类GatewayServiceApplication 上加@EnableEurekaClient 注解,开启EurekaClient的功能,加上@EnableZuulProxy注解,开启Zuul代理功能,代码如下:

@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
public class GatewayServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayServiceApplication.class, args);
    }
}

测试:

完整的项目搭建完毕,依次启动eureka-server.zipkin-serveruser service和gateway-service。在浏览器上访问http:/localhost:5000/user-api/user/hi,浏览器显示如下:

springcloud 链路追踪和日志 springcloud链路追踪原理_链路追踪


访问http://localhost:9411,即访问Zipkin的展示界面:

springcloud 链路追踪和日志 springcloud链路追踪原理_链路追踪_02


这个界面用于展示Zipkin Server收集的链路数据,可以根据服务名、开始时间、结束时间、请求消耗的时间等条件来查找。单击"Find Tracks"按钮,从图中可知请求的调用情况,例如请求的调用时间、消耗时间,以及请求调用的链路情况:

springcloud 链路追踪和日志 springcloud链路追踪原理_Zipkin_03

单击“Dependences"按钮,可以查看服务的依赖关系。在本案例中,gateway-service将请求转发到了user-service, 这两个服务的依赖关系如图:

springcloud 链路追踪和日志 springcloud链路追踪原理_spring_04