目录
1 分布式计算八大误区
网络可靠。
延迟为零。
带宽无限。
网络绝对安全。
网络拓扑不会改变。
必须有一名管理员。
传输成本为零。
网络同质化。(操作系统,协议)
2 链路追踪的必要性
如果能跟踪每个请求,中间请求经过哪些微服务,请求耗时,网络延迟,业务逻辑耗时等。我们就能更好地分析系统瓶颈、解决系统问题。因此链路跟踪很重要。
我们自己思考解决方案:在调用前后加时间戳。捕获异常。
链路追踪目的:解决错综复杂的服务调用中链路的查看。排查慢服务。
市面上链路追踪产品,大部分基于google的Dapper论文。
zipkin,twitter开源的。是严格按照谷歌的Dapper论文来的。
pinpoint 韩国的 Naver公司的。
Cat 美团点评的
EagleEye 淘宝的
3 链路追踪要考虑的几个问题
- 探针的性能消耗。尽量不影响 服务本尊。
- 易用。开发可以很快接入,别浪费太多精力。
- 数据分析。要实时分析。维度足够。
4 Sleuth简介
Sleuth是Spring cloud的分布式跟踪解决方案。
-
span(跨度),基本工作单元。一次链路调用,创建一个span,
span用一个64位id唯一标识。包括:id,描述,时间戳事件,spanId,span父id。
span被启动和停止时,记录了时间信息,初始化span叫:root span,它的span id和trace id相等。
-
trace(跟踪),一组共享“root span”的span组成的树状结构 称为 trace,trace也有一个64位ID,trace中所有span共享一个trace id。类似于一颗 span 树。
-
annotation(标签),annotation用来记录事件的存在,其中,核心annotation用来定义请求的开始和结束。
- CS(Client Send客户端发起请求)。客户端发起请求描述了span开始。
- SR(Server Received服务端接到请求)。服务端获得请求并准备处理它。SR-CS=网络延迟。
- SS(Server Send服务器端处理完成,并将结果发送给客户端)。表示服务器完成请求处理,响应客户端时。SS-SR=服务器处理请求的时间。
- CR(Client Received 客户端接受服务端信息)。span结束的标识。客户端接收到服务器的响应。CR-CS=客户端发出请求到服务器响应的总时间。
其实数据结构是一颗树,从root span 开始。
5 快速入门
5.1 Sleuth入门搭建
在 api_gateway_server 工程模块基础上扩展
5.1.1 引入坐标
<!--sleuth链路追踪-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
5.1.2 yml配置
各个微服务的yml配置文件都要加入日志
logging:
level:
root: info
org.springframework.web.servlet.DispatcherServlet: DEBUG
org.springframework.cloud.sleuth: DEBUG
- 每个微服务都需要添加如上的配置
5.1.3 访问测试
- 启动微服务并调用,可以在控制台观察到sleuth的日志输出
- 其中
7c8d741c36af2723
是TraceId,后面的是SpanId,依次调用有一个全局的TraceId,将调用链路串起来。仔细分析每个微服务的日志,不难看出请求的具体过程。
查看日志文件并不是一个很好的方法,当微服务越来越多日志文件也会越来越多,通过Zipkin
可以将日
志聚合,并进行可视化展示和全文检索 - 各个服务的控制台都可以看到日志的输出
6 项目整合Zipkin
6.1 docker 安装 zipkin
docker run -d -p 9411:9411 openzipkin/zipkin
6.2 在pom中添加依赖
<!--zipkin 依赖也同时包含了 sleuth,可以省略 sleuth 的引用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
6.3 在application.yml添加如下配置
spring:
zipkin:
base-url: http://192.168.2.190:9411/ # zipkin 服务器的地址
discovery-client-enabled: false # 关闭服务发现,否则 Spring Cloud 会把 zipkin 的 url 当做服务名称
sender:
type: web # 设置使用 http 的方式传输数据
sleuth:
sampler:
probability: 1 # 设置抽样采集率为 100% ,默认为 0.1 ,即 10%
6.4 本地测试
启动项目,所有功能都跑一遍,然后访问 zipkin 服务器地址,显示如下:
7 sleuth+elk聚合日志
sleuth配置
- 在微服务项目引入下列依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
- 配置文件
application.yml
增加
logging:
level:
root: INFO
org.springframework.web.servlet.DispatcherServlet: DEBUG
org.springframework.cloud.sleuth: DEBUG
- 依次启动
hello项目
和helloworld项目
,在浏览器输入http://localhost:8020/message
后两个项目的控制台输出如下日志
helloworld日志
hello 日志.png
其中a7c81616d25c1a88
是TraceId,后面跟着的是SpanId,依次调用有一个全局的TraceId,将调用链路串起来。
查看日志文件并不是一个很好的方法,当微服务越来越多日志文件也会越来越多,通过ELK可以将日志聚合,并进行可视化展示和全文检索。
sleuth配合elk使用
ELK是一款日志分析系统,它是Logstash
+ElasticSearch
+Kibana
的技术组合,它可以通过logstash收集各个微服务日志,并通过Kibana进行可视化展示,而且还可以对大量日志信息通过ElasticSearch进行全文检索。
ELK.png
操作步骤:
- 安装ELK,我使用了docker安装了ELK,具体安装步骤可参考这篇文章:
区别是在启动logstash时,指定了日志来源路径
/opt/logstash/bin/logstash -e
'input { file { codec => json path => "/opt/build/*.json" } }
output { elasticsearch { hosts => ["localhost"] } }'
- 项目添加依赖
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.9</version>
</dependency>
- 在src/java/resources目录下新建logback-spring.xml,配置如下内容
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<springProperty scope="context"
name="springAppName"
source="spring.application.name"/>
<property name="LOG_FILE"
value="${BUILD_FOLDER:-build}/${springAppName}"/>
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p})
%clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan}
%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="filelog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gz</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>7</MaxHistory>
</rollingPolicy>
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- 使用json格式保存日志文件 -->
<appender name="jsonlog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}.json</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_FILE}.json.%d{yyyy-MM-dd}.gz</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>7</MaxHistory>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>UTC</timeZone>
</timestamp>
<pattern>
<pattern>
{
"severity": "%level",
"service": "${springAppName:-}",
"trace": "%X{X-B3-TraceId:-}",
"span": "%X{X-B3-SpanId:-}",
"parent": "%X{X-B3-ParentSpanId:-}",
"exportable": "%X{X-Span-Export:-}",
"pid": "${PID:-}",
"thread": "%thread",
"class": "%logger{40}",
"rest": "%message"
}
</pattern>
</pattern>
</providers>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<root level="INFO">
<appender-ref ref="console"/>
<appender-ref ref="jsonlog"/>
<appender-ref ref="filelog"/>
</root>
</configuration>
因为上面的日志文件用到spring.application.name,所以需要项目名称的配置挪到bootstrap.yml。
- 测试结果,在浏览器输入
http://localhost:8020/message
发起几次调用后,打开http://localhost:5601后
看到上述的Kibana页面,说明可以正常使用ELK查询,分析跟踪日志。