SpringCloud - Spring Cloud Sleuth(1) - 简介

参考:https://cloud.spring.io/spring-cloud-static/Edgware.SR5/multi/multi__introduction.html

Spring Cloud Sleuth为Spring Cloud实现了分布式跟踪解决方案。

1. 术语
Spring Cloud Sleuth借用了Dapper的术语。

Span:基本工作单元。例如,发送RPC是一个新的span,就像向RPC发送响应一样。spans由一个唯一64位ID(span)和另一个64位ID(trace,span为其一部分)来标识。 Spans还有其他数据,例如描述、带时间戳的事件,键值注解(标签),导致它们的span的ID以及进程ID(通常是IP地址)。

Spans启动和停止,他们跟踪他们的时间信息。创建span后,必须在将来的某个时刻停止它。

提示:启动跟踪的初始范围称为root span。该span的span id值等于trace id。

Trace:一组span形成树状结构。例如,如果正在运行分布式大数据存储,则可能会由put请求形成跟踪。

Annotation:用于及时记录事件的存在。用于定义请求的开始和停止的一些核心注解是:

  • cs:Client Sent - 客户端已发出请求。此注解描述了span的开始。
  • sr:Server Received - 服务器端获得请求并将开始处理它。如果从该时间戳中减去cs时间戳,则会收到网络延迟。
  • ss:Server Sent   - 在请求处理完成时注解(当响应被发送回客户端时)。如果从该时间戳中减去sr时间戳,则将接收服务器端处理请求所需的时间。
  • cr:Client Received - 表示span结束。客户端已成功收到服务器端的响应。如果从该时间戳中减去cs时间戳,则将接收客户端从服务器接收响应所需的全部时间。

使用Zipkin注解可视化SpanTrace在系统中的变化:

注解的每种颜色表示跨度(7个跨度 - 从A到G)。 如果在说明中有此类信息:

Trace Id = X
Span Id = D
Client Sent

这意味着当前跨度的Trace-Id设置为X,Span-Id设置为D.它还发出了Client Sent事件。

span的父/子关系可能情况的可视化图如下所示:

2. 目的
在以下部分中,将思考上图中的示例。

2.1 使用Zipkin进行分布式跟踪
总共有7个span。 如果你去查看Zipkin的跟踪记录,你会在第二个跟踪中看到这个数字:

 但是,如果选择特定的跟踪,那么将看到4个span:

注:选择特定跟踪时,将看到合并的span。这意味着,如果通过Server Received和Server Sent / Client Received以及Client Sent 注解向Zipkin发送了2个span,那么它们将显示为单个span。

在这种情况下,为什么7和4个span之间存在差异?

  • 2个span来自http:/start span。它具有服务器接收(SR)和服务器发送(SS)注解。
  • 2个span来自从service1到service2的RPC调用到http:/foo端点。它在service1端具有客户端发送(CS)和客户端接收(CR)注解。它还在service2端具有服务器接收(SR)和服务器发送(SS)注解。物理上有2个span,但它们形成与RPC调用相关的1个逻辑span。
  • 从Service2到service3到http:/bar端点的RPC调用有2个span。它在service2端具有客户端发送(CS)和客户端接收(CR)注解。它还在service3端具有服务器接收(SR)和服务器发送(SS)注释。物理上有2个span,但它们形成与RPC调用相关的1个逻辑span。
  • 2个span来自从service2到service4的RPC调用到http:/baz端点。它在service2端具有客户端发送(CS)和客户端接收(CR)注解。它还在service4端具有服务器接收(SR)和服务器发送(SS)注解。物理上有2个span,但它们形成与RPC调用相关的1个逻辑span。

因此,如果我们计算物理span,我们从http:/start得到1,从service1调用service2得到2,service2调用service3的到2,从service2调用service4得到2。总共7个span。

从逻辑上讲,我们看到 Total Spans: 4 的信息,因为我们有1个与service1的传入请求相关的span和3个与RPC调用相关的span。

2.2 可视化错误
Zipkin允许可视化跟踪中的错误。当抛出一个异常并且没有被捕获时,我们就在Zipkin可以正确着色的span上设置适当的标签。可以在跟踪列表中看到一条红色的迹线,那是因为抛出了异常。

如果单击该跟踪,则会看到类似的图片:

然后,如果单击其中一个span,将看到以下内容

如图所示,可以轻松查看错误的原因以及与之相关的整个堆栈跟踪。

2.3 实例

Zipkin中的依赖关系图如下所示:

2.4 日志关联
trace id为2485ec27856c56f4,对这四个应用程序的日志进行聚合时 将获得以下内容:

service1.log:2016-02-26 11:15:47.561  INFO [service1,2485ec27856c56f4,2485ec27856c56f4,true] 68058 --- [nio-8081-exec-1] i.s.c.sleuth.docs.service1.Application   : Hello from service1. Calling service2
service2.log:2016-02-26 11:15:47.710  INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application   : Hello from service2. Calling service3 and then service4
service3.log:2016-02-26 11:15:47.895  INFO [service3,2485ec27856c56f4,1210be13194bfe5,true] 68060 --- [nio-8083-exec-1] i.s.c.sleuth.docs.service3.Application   : Hello from service3
service2.log:2016-02-26 11:15:47.924  INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application   : Got response from service3 [Hello from service3]
service4.log:2016-02-26 11:15:48.134  INFO [service4,2485ec27856c56f4,1b1845262ffba49d,true] 68061 --- [nio-8084-exec-1] i.s.c.sleuth.docs.service4.Application   : Hello from service4
service2.log:2016-02-26 11:15:48.156  INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application   : Got response from service4 [Hello from service4]
service1.log:2016-02-26 11:15:48.182  INFO [service1,2485ec27856c56f4,2485ec27856c56f4,true] 68058 --- [nio-8081-exec-1] i.s.c.sleuth.docs.service1.Application   : Got response from service2 [Hello from service2, response from service3 [Hello from service3] and from service4 [Hello from service4]]

如果使用的是日志聚合工具,如Kibana、Splunk等,可以给发生的事件排序。 Kibana的一个例子如下所示:

 

如果你想使用Logstash,这里是Logstash的Grok模式:

filter {
       # pattern matching logback pattern
       grok {
              match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
       }
}

如果要将Grok与Cloud Foundry中的日志一起使用,则必须使用以下模式:

filter {
       # pattern matching logback pattern
       grok {
              match => { "message" => "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
       }
}

使用Logstash的JSON Logback
通常,不希望将日志存储在文本文件中,而是存储在Logstash可以立即选择的JSON文件中。 为此,必须执行以下操作(为了便于阅读,我们以 groupId:artifactId:version 格式表示依赖项)。

(1)依赖关系设置

  • 确保Logback位于类路径上(ch.qos.logback:logback-core)
  • 添加Logstash Logback - 4.6版本示例:net.logstash.logback:logstash-logback-encoder:4.6

(2)Logback设置

  • 将来自应用程序的信息以JSON格式记录到build/$ {spring.application.name}.json文件中
  • 已注释掉两个额外的appender  - 控制台和标准日志文件
  • 具有与上一节中介绍的相同的日志记录模式

示例:

<?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"/>
	<!-- Example for logging into the build folder of your project -->
	<property name="LOG_FILE" value="${BUILD_FOLDER:-build}/${springAppName}"/>​

	<!-- 可以覆盖下面的配置,使用自定义的配置 -->
	<property name="CONSOLE_LOG_PATTERN"
			  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} %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>
			<charset>utf8</charset>
		</encoder>
	</appender>

	<!-- 文件日志 -->​
	<appender name="flatfile" 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>
			<pattern>${CONSOLE_LOG_PATTERN}</pattern>
			<charset>utf8</charset>
		</encoder>
	</appender>
	​
	<!-- 以JSON格式添加至日志文件 -->
	<appender name="logstash" 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>
	</appender>
	​
	<root level="INFO">
		<appender-ref ref="console"/>
		<!-- uncomment this to have also JSON logs -->
		<!--<appender-ref ref="logstash"/>-->
		<!--<appender-ref ref="flatfile"/>-->
	</root>
</configuration>

注:如果使用的是自定义logback-spring.xml,则必须在bootstrap中传递 spring.application.name 而不是 application属性文件。否则,自定义logback文件将无法正确读取该属性。

2.5 传播span上下文
span上下文是必须传播到跨进程边界的任何子span的状态。span上下文的一部分是Baggage。trace和span ID是span上下文的必需部分。Baggage是可选部分。

Baggage是存储在span上下文中的一组键值对。Baggage与trace一起移动并附在每个span上。如果HTTP报头以 baggage- 为前缀,Spring Cloud Sleuth将理解报头是Baggage相关的,并且对于消息,它以 baggage_ 开头。

注:目前对baggage的数量或大小没有限制。但是,太多可能会降低系统吞吐量或增加RPC延迟。在极端情况下,由于超出传输级别的消息或报头容量,它可能会导致应用程序崩溃。

在span上设置baggage的示例:

Span initialSpan = this.tracer.createSpan("span");
initialSpan.setBaggageItem("foo", "bar");
initialSpan.setBaggageItem("UPPER_CASE", "someValue");

Baggage与span标签

  • Baggage和trace一起移动(即每个子span包含其父span的baggage)。 Zipkin不了解baggage,甚至不会收到这些信息。
  • 标签附加到特定的span --- 它们仅针对特定span呈现。 但是,可以按标记搜索以查找trace,其中存在具有搜索标记值的span。

如果希望能够根据baggage查找span,则应在root span中添加相应的条目作为标记。

@Autowired Tracer tracer;

Span span = tracer.getCurrentSpan();
String baggageKey = "key";
String baggageValue = "foo";
span.setBaggageItem(baggageKey, baggageValue);
tracer.addTag(baggageKey, baggageValue);

3. 集成到项目中
注:要确保在Zipkin中正确显示应用程序名称,在bootstrap.yml中设置spring.application.name属性。

3.1只有Sleuth(日志相关)
如果想在没有Zipkin集成的情况下仅从Spring Cloud Sleuth获取,只需将spring-cloud-starter-sleuth模块添加到项目中。

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

   <dependency> 2
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-sleuth</artifactId>
   </dependency>

3.2 通过HTTP使用集成了Zipkin的Sleuth
如果想要Sleuth和Zipkin,只需添加spring-cloud-starter-zipkin依赖项。

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

   <dependency> 2
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-zipkin</artifactId>
   </dependency>

3.3 通过RabbitMQ或Kafka使用集成了Zipkin的Sleuth
如果想使用RabbitMQ或Kafka而不是http,添加spring-rabbit或spring-kafka依赖项。默认目标名称是 zipkin。

注意:spring-cloud-sleuth-stream已经废弃了,并且与这些目标不兼容。

如果想要在RabbitMQ上使用Sleuth,添加spring-cloud-starter-zipkin和spring-rabbit依赖项。

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

   <dependency> 2
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-zipkin</artifactId>
   </dependency>
   <dependency> 3
       <groupId>org.springframework.amqp</groupId>
       <artifactId>spring-rabbit</artifactId>
   </dependency>

猜你喜欢

转载自blog.csdn.net/mytt_10566/article/details/84930099