一、Spring Cloud Sleuth介绍

1、分布式服务跟踪必要性

现今互联网环境中,微服务系统越来越庞大、复杂,微服务间的调用关系也越加复杂。往往一个请求,会出发系统后台多个微服务协同工作得到最终结果,那么在复杂的调用网中,任何一个服务出现问题,都会导致整体功能出错。
这时,微服务跟踪工具应运而生,其在整体微服务应用中能跟踪一个请求的整体流程。并提供数据采集,数据传输,数据存储,数据分析,数据可视化功能。微服务跟踪工具捕获的这些跟踪数据,就能构建出整个微服务调用链视图,为调试和监控微服务系统提供帮助。
Spring Cloud Sleuth就是这样的微服务跟踪工具。其特点为:
提供链路追踪:通过Sleuth可以很清楚的看到一次请求经过哪些服务调用,可以方便理清服务间调用关系。
性能分析、数据分析、优化链路:通过Sleuth可以很方便的看出每个采样请求的耗时,分析出哪些服务调用比较耗时,可以为微服务系统的调优提供数据支撑。
可视化视图:可以提供可视化视图,更直观的查看采样数据。

2、基于ELK收集分布式微服务跟踪数据

Sleuth是基于logback实现数据跟踪的。在默认情况下,Sleuth是基于日志向控制台输出跟踪内容。不利于管理,统计,查看,分析。在控制台中输出跟踪内容会严重影响系统性能。如果将跟踪数据记录在logback对应的日志文件中,也有问题:logback是分散的,是集成在每个服务应用中的,那么日志文件也是分散的, 也不利于跟踪信息的查看,管理,分析。
所以Sleuth提供了集中式的跟踪数据存储方案。可以使用ELK来实现logback跟踪信息的收集,存储。实质上是使用logstash来做数据的收集,用ElasticSearch做数据的存储,使用Kibana做数据的视图显示。
使用ELK收集跟踪数据,必须依赖Logback日志工具,也就是必须提供logback.xml配置文件,并且日志级别建议调整为DEBUG。

2.1 结构图

Sleuth分布式服务跟踪 - 图1

2.2 搭建过程

2.2.1 LogStash配置

连接LogStash容器:
docker exec -it logstash bash
编辑配置文件:
vi /usr/share/logstash/pipeline/logstash.conf
具体内容如下:

input {
tcp {
mode => “server”
port => 4560
}
}
filter {
}
output {
elasticsearch {
action => “index” #The operation on ES
hosts => “192.168.137.128:9200” #ElasticSearch host, can be array.
index => “bjsxt_sleuth” #The index to write data to.
}
}

2.2.2 重启Logstash容器

docker restart logstash

2.2.3 修改微服务应用

2.2.3.1 新增依赖

在需要跟踪微服务信息的应用中增加下述依赖



org.springframework.cloud
spring-cloud-starter-sleuth



net.logstash.logback
logstash-logback-encoder
5.0

2.2.3.2 提供logback配置

在需要跟踪微服务信息的应用中增加下述配置,配置文件命名为logback.xml,所在位置是classpath。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!--该日志将日志级别不同的log信息保存到不同的文件中 -->
  3. <configuration>
  4. <include resource="org/springframework/boot/logging/logback/defaults.xml" />
  5. <springProperty scope="context" name="springAppName"
  6. source="spring.application.name" />
  7. <!-- 日志在工程中的输出位置 -->
  8. <property name="LOG_FILE" value="${BUILD_FOLDER:-build}/${springAppName}" />
  9. <!-- 控制台的日志输出样式 -->
  10. <property name="CONSOLE_LOG_PATTERN"
  11. value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}" />
  12. <!-- 控制台输出 -->
  13. <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  14. <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
  15. <level>INFO</level>
  16. </filter>
  17. <!-- 日志输出编码 -->
  18. <encoder>
  19. <pattern>${CONSOLE_LOG_PATTERN}</pattern>
  20. <charset>utf8</charset>
  21. </encoder>
  22. </appender>
  23. <!-- 为logstash输出的JSON格式的Appender -->
  24. <appender name="logstash"
  25. class="net.logstash.logback.appender.LogstashTcpSocketAppender">
  26. <destination>192.168.137.128:4560</destination>
  27. <!-- 日志输出编码 -->
  28. <encoder
  29. class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
  30. <providers>
  31. <timestamp>
  32. <timeZone>UTC</timeZone>
  33. </timestamp>
  34. <pattern>
  35. <pattern>
  36. {
  37. "severity": "%level",
  38. "service": "${springAppName:-}",
  39. "trace": "%X{X-B3-TraceId:-}",
  40. "span": "%X{X-B3-SpanId:-}",
  41. "exportable": "%X{X-Span-Export:-}",
  42. "pid": "${PID:-}",
  43. "thread": "%thread",
  44. "class": "%logger{40}",
  45. "rest": "%message"
  46. }
  47. </pattern>
  48. </pattern>
  49. </providers>
  50. </encoder>
  51. </appender>
  52. <!-- 日志输出级别 -->
  53. <root level="DEBUG">
  54. <appender-ref ref="console" />
  55. <appender-ref ref="logstash" />
  56. </root>
  57. </configuration>

2.2.3.3 特殊情况

如果项目中有其他与Redis相关的项目,则需要在项目启动类型上增加注解属性,具体如下:

@SpringBootApplication(exclude = {TraceRedisAutoConfiguration.class})
@EnableFeignClients
@EnableDistributedTransaction
public class FrontendSearchApplication {
public static void main(String[] args) {
System.setProperty(“es.set.netty.runtime.available.processors”, “false”);
SpringApplication.run(FrontendSearchApplication.class,args);
}
}

原因:Sleuth也可以监听Redis数据库的访问流程。当项目中有和Redis相关的操作启动器时(如spring session),则会因为两个技术相互抢占Redis访问锁,造成死锁逻辑,最终导致访问Redis服务器超时,此时关闭Sleuth自带的Redis数据库自动装配即可解决问题。
上述所有步骤操作完毕后,使用ELK实现跟踪数据收集的案例就改造完毕了,可以启动并观察ElasticSearch中对应索引的内容。

2.3 索引中数据含义

查看跟踪数据的时候,可以根据索引中字段message搜索,主要观察的是包含Trace相关内容的数据,数据格式如下:

{
“@timestamp”:”2018-11-21T14:26:31.264+00:00”, #时间戳
“severity”:”DEBUG”, # 收集的跟踪数据日志级别
“service”:”e-book-user-provider”, # 当前跟踪的服务名称
“trace”:”c82b2d23e6c3245b”, # 一个请求的完整链路唯一标记
“span”:”95b7128dec20f57f”, # 一个请求中某一执行节点的唯一标记
“exportable”:”false”,
“pid”:”8144”, # 进程编号
“thread”:”http-nio-9002-exec-8”, # 线程名称
“class”:”o.s.c.sleuth.instrument.web.TraceFilter”, # 记录日志的类名
“rest”:
“” # 具体的执行消息
}

二、使用Zipkin实现分布式跟踪

1 、ZipKin

Zipkin 是一个开放源代码分布式的跟踪系统,由Twitter公司开源,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。
每个服务向zipkin报告计时数据,例如用户每次请求服务的处理时间等,可方便的监测系统中存在的瓶颈。
zipkin会根据调用关系通过Zipkin UI生成依赖关系图。

2 、Zipkin执行原理图

Sleuth分布式服务跟踪 - 图2
Collector 收集器、Storage 存储、API、UI 用户界面等几部分构成了 Zipkin Server 部分

3 、基于Docker搭建Zipkin

Zipkin2.x版本以后,官方不推荐自定义提供Zipkin服务器。建议使用官方提供的Docker镜像搭建。具体步骤如下:

3.1 下载镜像

docker pull openzipkin/zipkin

3.2 创建容器

docker run -d —name zipkin-server -p 9411:9411 openzipkin/zipkin
zipkin官方提供的镜像默认占用端口9411

3.3 检查容器状态

docker logs -f zipkin-server

4 、修改被收集跟踪信息的微服务应用

使用Sleuth采集跟踪数据的服务应用不需要在代码层面做任何改动,Sleuth|zipkin是一个高度封装的应用工具,提供依赖资源和配置信息即可。
在所有需要使用Sleuth|zipkin采集跟踪数据的服务应用统一修改下述内容。

4.1 pom依赖

增加下述依赖:

<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-binder-rabbit</artifactId> </dependency>

4.2 修改配置文件

配置文件增加下述配置。

| spring: rabbitmq:
host: 192.168.137.128
port: 5672
username: bjsxt
password: bjsxt
zipkin:
base-url: http://192.168.137.128:9411/

**sender**:<br />      **type**: _web<br />  _**sleuth**:<br />    **sampler**:<br />      **probability**: 1 |

| —- |

4.3 删除logback配置

如果应用曾经使用logback+ELK收集服务数据,则需要删除logback配置文件。

上述内容修改完毕后,重新启动微服务即可。

5 、基于Zipkin观察跟踪数据

使用浏览器访问Zipkin服务端应用。地址是:
http://ip:9411/zipkin
Sleuth分布式服务跟踪 - 图3
Sleuth分布式服务跟踪 - 图4