案例代码:https://github.com/q279583842q/springcloud-e-book
一、Sleuth介绍
为什么要使用微服务跟踪?它解决了什么问题?
1.微服务的现状?
微服务的现状
随着业务的发展,单体架构变为微服务架构,并且系统规模也变得越来越大,各微服务间的调用关系也变得越来越复杂。
多服务协同工作
在微服务的应用中,一个由客户端发起的请求在后端系统中会经过多个不同的微服务调用来协同产生最后的请求结果
复杂的调用链条容易出错
在复杂的微服务架构系统中,几乎每一个前端请求都会形成一个复杂的分布式服务调用链路,在每条链路中任何一个依赖服务出现延迟超时或者错误都有可能引起整个请求最后的失败
例如:
在微服务系统中,一个来自用户的请求,请求先达到前端A(如前端界面)然后通过远程调用,到达系统中间件B,C(负载均衡,网关等),最后达到后端服务D,E,后端经过一系列的业务逻辑计算最后将数据返回给用户,对于这样一个请求,经历了这么多个服务,怎么样将它的请求过程的数据记录下来呢?这就需要用到服务链路追踪
2.微服务跟踪解决了什么问题?
微服务跟踪(sleuth)其实是一个工具,它在整个分布式系统中能跟踪一个用户请求的过程(包括数据采集,数据传输,数据存储,数据分析,数据可视化),捕获这些跟踪数据,就能构建微服务的整个调用链的视图,这是调试和监控微服务的关键工具。
SpringCloudSleuth有4个特点
提供链路追踪 | 通过sleuth可以很清楚的看出一个请求经过了哪些服务, 可以方便的理清服务局的调用关系 |
性能分析 | 通过sleuth可以很方便的看出每个采集请求的耗时, 分析出哪些服务调用比较耗时,当服务调用的耗时 随着请求量的增大而增大时,也可以对服务的扩容提 供一定的提醒作用 |
数据分析 优化链路 | 对于频繁地调用一个服务,或者并行地调用等, 可以针对业务做一些优化措施 |
可视化 | 对于程序未捕获的异常,可以在zipkpin界面上看到 |
二、Sleuth案例
我们通过一个简单的微服务调用案例来演示下Sleuth是怎么跟踪请求调用的,案例结构图如下:
1.创建sleuth-product-service服务
1.1 创建服务
1.2 创建pojo
此处的Product类以及相关代码在GitHub上
1.3 创建service接口
@RequestMapping("/product")
public interface ProductService {
@RequestMapping(value="findAll",method=RequestMethod.GET)
public List<Product> findAll();
}
2.创建sleuth-product-provider服务
2.1 创建项目
2.2 pom文件
注意添加sleuth的依赖
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.bobo</groupId>
<artifactId>sleuth-product-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.bobo</groupId>
<artifactId>sleuth-product-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</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>
2.3 配置文件
没有特殊的
spring.application.name=sleuth-product
server.port=9001
#\u8BBE\u7F6E\u670D\u52A1\u6CE8\u518C\u4E2D\u5FC3\u5730\u5740\uFF0C\u6307\u5411\u53E6\u4E00\u4E2A\u6CE8\u518C\u4E2D\u5FC3
eureka.client.serviceUrl.defaultZone=http://dpb:123456@eureka1:8761/eureka/,http://dpb:123456@eureka2:8761/eureka/
#--------------db----------------
mybatis.type-aliases-package=com.book.product.pojo
mybatis.mapper-locations=classpath:com/bobo/product/mapper/*.xml
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/book-product?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
spring.datasource.username=root
spring.datasource.password=123456
注意添加日志配置文件,日志级别设置为debug
2.4 业务代码
业务代码提供了对商品数据的查询。
3.创建sleuth-consumer服务
3.1 创建项目
3.2 pom文件
统一注意添加sleuth的依赖
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
</parent>
<groupId>com.bobo</groupId>
<artifactId>sleuth-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- 添加Feign坐标 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!-- 添加e-book-product-service坐标 -->
<dependency>
<groupId>com.bobo</groupId>
<artifactId>sleuth-product-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.3 配置文件
没有特殊的配置
spring.application.name=e-book-consumer
server.port=9010
#\u8BBE\u7F6E\u670D\u52A1\u6CE8\u518C\u4E2D\u5FC3\u5730\u5740\uFF0C\u6307\u5411\u53E6\u4E00\u4E2A\u6CE8\u518C\u4E2D\u5FC3
eureka.client.serviceUrl.defaultZone=http://dpb:123456@eureka1:8761/eureka/,http://dpb:123456@eureka2:8761/eureka/
3.4 业务代码
通过Feign实现对商品服务的调用,具体代码见GitHub,地址在头部
4.服务跟踪
先启动product服务,然后启动consumer服务,访问:http://localhost:9010/find
请求访问成功,注意查看控制台信息
consumer的控制器
product的控制台信息
5.Sleuth 日志分析
Created new Feign span [Trace: cbe97e67ce162943, Span: bb1798f7a7c9c142, Parent: cbe97e67ce162943, exportable:false]
# 调用 product服务
2019-06-30 09:43:24.022 [http-nio-9010-exec-8] DEBUG o.s.c.s.i.web.client.feign.TraceFeignClient - The modified request equals GET http://localhost:9001/product/findAll HTTP/1.1
X-B3-ParentSpanId: cbe97e67ce162943
X-B3-Sampled: 0
X-B3-TraceId: cbe97e67ce162943
X-Span-Name: http:/product/findAll
X-B3-SpanId: bb1798f7a7c9c142
# product中的日志输出
## product 被调用
[findAll] to a span [Trace: cbe97e67ce162943, Span: bb1798f7a7c9c142, Parent: cbe97e67ce162943, exportable:false]
Adding a class tag with value [ProductController] to a span [Trace: cbe97e67ce162943, Span: bb1798f7a7c9c142, Parent: cbe97e67ce162943, exportable:false]
## 调用数据库操作
org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@42858a0e] was not registered for synchronization because synchronization is not active
o.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
o.m.spring.transaction.SpringManagedTransaction - JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@7981208d]]] will not be managed by Spring
com.bobo.mapper.ProductMapper.selectByExample - ==> Preparing: select id, name, status, price, deleted, create_time, update_time from product
com.bobo.mapper.ProductMapper.selectByExample - ==> Parameters:
com.bobo.mapper.ProductMapper.selectByExample - <== Total: 3
## 结束请求
o.s.cloud.sleuth.instrument.web.TraceFilter - Closing the span [Trace: cbe97e67ce162943, Span: bb1798f7a7c9c142, Parent: cbe97e67ce162943, exportable:false] since the response was successful
# consumer 中调用服务结束
Closing Feign span and logging CR [Trace: cbe97e67ce162943, Span: bb1798f7a7c9c142, Parent: cbe97e67ce162943, exportable:false]
Closing Feign span [Trace: cbe97e67ce162943, Span: bb1798f7a7c9c142, Parent: cbe97e67ce162943, exportable:false]
如上,通过日志我们可以看出服务调用的相关过程
trace | 从客户发起请求(request)抵达被追踪系统的边界开始,到被追踪系统向客户返回响应(response)为止的整个过程 |
span | 每个trace中会调用若干个服务,为了记录调用了哪些服务,以及每次调用的消耗时间等信息,在每次调用服务时,埋入一个调用记录 |
X-B3-ParentSpanId | 标识当前工作单元所属的上一个工作单元 |
X-B3-Sampled | 是否采样,1表示需要被输出,0表示不需要被输出 |
X-B3-TraceId | 一条请求链路(trace)的唯一标识,必须值 |
X-Span-Name | 工作单元的名称,例如: http:/product/findAll |
X-B3-SpanId | 一个工作单元(span)的唯一标识,必须值 |