SPRING CLOUD微服务实战笔记--分布式服务跟踪:Spring Cloud Sleuth

Spring Cloud Sleuth

  • 快速入门
    • 准备工作
    • 实现跟踪
  • 跟踪原理
  • 与Logstash整合
  • 与ZipKin整合
    • HTTP收集
    • 消息中间件收集
    • 数据存储

快速入门

准备工作

  • 服务注册中心:eureka-server,可以直接使用之前的工程
  • 微服务应用:trace-1,实现一个REST接口/trace-1,调用该接口后将触发对trace-2应用的调用
    具体实现如下:
    1)创建一个基础的Spring Boot应用,在pom.xml中增加依赖:
<dependencies>
	<dependency>
		<groupId>org.springframework.cloudgroupId>
		<artifactId>spring-cloud-starter-eurekaartifactId>
		<version>1.4.6.RELEASEversion>
	dependency>
	<dependency>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-webartifactId>
	dependency>
	<dependency>
		<groupId>org.springframework.cloudgroupId>
		<artifactId>spring-cloud-starter-ribbonartifactId>
		<version>1.4.6.RELEASEversion>
	dependency>
	<dependency>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-testartifactId>
		<scope>testscope>
	dependency>
dependencies>

2)创建主类,实现/trace-1接口,并使用RestTemplate调用trace-2应用的接口

@RestController
@EnableEurekaClient
@SpringBootApplication
public class HelloApplication {
	private final Logger logger = LoggerFactory.getLogger(getClass());
	@Bean
	@LoadBalanced
	RestTemplate restTemplate(){
		return new RestTemplate();
	}
	@RequestMapping(value = "/trace-1",method = RequestMethod.GET)
	public String trace(){
		logger.info("===call trace-1====");
		return restTemplate().getForEntity("http://trace-2/trace-2",String.class).getBody();
	}
	public static void main(String[] args) {
		SpringApplication.run(HelloApplication.class, args);
	}
}

3)在application.properties中,将eureka.client.serviceUrl.defaultZone参数指向eureka-server地址

spring.application.name=trace-1
server.port=9101
eureka.client.service-url.defaultZone=http://localhost:1111/eureka
  • 微服务应用:trace-2,实现一个REST接口/trace-2,供trace-1调用
    1)创建一个基础的SpringBoot应用,pom.xml的依赖与trace-1相同
    2)创建应用主类,并实现/trace-2接口,具体如下:
@RestController
@EnableEurekaClient
@SpringBootApplication
public class HelloApplication {
   private final Logger logger = LoggerFactory.getLogger(getClass());
   @RequestMapping(value = "/trace-2",method = RequestMethod.GET)
   public String trace(){
   	logger.info("=======");
   	return "Trace";
   }
   public static void main(String[] args) {
   	SpringApplication.run(HelloApplication.class, args);
   }
}

3)在application.properties中,将eureka.client.serviceUrl.defaultZone参数指向eureka-server地址,并设置不同的应用名与端口

spring.application.name=trace-2
server.port=9102
eureka.client.service-url.defaultZone=http://localhost:1111/eureka
  • 启动eureka-server,trace-1,trace-2三个应用,发送请求http://localhost:9101/trace-1,可以看到返回值Trace,同时trace-1控制台打印===call trace-1====,trace-2控制台打印=======

实现跟踪

在trace-1和trace-2的pom.xml依赖管理中增加spring-cloud-starter-sleuth依赖即可

<dependency>
	<groupId>org.springframework.cloudgroupId>
	<artifactId>spring-cloud-starter-sleuthartifactId>
	<version>1.3.5.RELEASEversion>
dependency>

重启trace-1和trace-2后,控制台打印发生变化,

INFO [trace-1,ddcf47da76da68c7,ddcf47da76da68c7,false] 70283 --- [nio-9101-exec-1] ication$$EnhancerBySpringCGLIB$$84347891 : ===call trace-1====
INFO [trace-2,ddcf47da76da68c7,248958640a14c496,false] 70280 --- [nio-9102-exec-1] ication$$EnhancerBySpringCGLIB$$84347891 : =======

可以看到增加了[trace-2,ddcf47da76da68c7,ddcf47da76da68c7,false]类似的日志信息,含义如下:

  • 第一个值:trace-1,记录应用的名称
  • 第二个值:ddcf47da76da68c7,Spring Cloud Sleuth生成的一个ID,成为TraceID,用来标识一条请求链路。一条请求链路包含一个TraceId
  • 第三个值:248958640a14c496,Spring Cloud Sleuth生成的另一个ID,成为Span ID,表示一个基本的工作单元,比如发送一个HTTP请求
  • 第四个值:false,表示是否要将该信息输出到Zipkin等服务中来收集和展示
    在一次服务请求链路的调用过程中,会保持并传递同一个Traceid,从而将整个分布于不同微服务进程中的请求跟踪信息串联起来。如上面例子,trace-1与trace-2同属于一个前端服务请求来源,所以它们的TraceID相同,处于同一条请求链路中。

跟踪原理

TraceID和SpanID是Spring Cloud Sleuth实现分布式服务跟踪的核心

  • 通过唯一跟踪标识TraceID的记录,能将所有请求过程的日志关联起来
  • SpanID可以统计各处理单元的时间延迟,还可以包含一些其他数据,比如事件名称,请求信息等
    spring-cloud-starter-sleuth组件在转发请求的Header中增加实现跟踪需要的重要信息,主要有下面几个:
  • X-B3-TraceId:一条请求链路(Trace)的唯一标识,必需的值
  • X-B3-SpanId:一个工作单元(Span)的唯一标识,必需的值
  • X-B3-ParentSpanId:标识当前工作单元所属的上一个工作单元,RootSpan(请求链路的第一个工作单元)的该值为空
  • X-Span-Name:工作单元的名称
    1.对trace-1和trace-2的实现进行修改
@RequestMapping(value = "/trace-1",method = RequestMethod.GET)
	public String trace(HttpServletRequest request){
		logger.info("=========,TraceId={},SpanId={},ParentSpanId={},Sampled={},SpanName={}"
				,request.getHeader("X-B3-TraceId"),request.getHeader("X-B3-SpanId")
				,request.getHeader("X-B3-ParentSpanId"),request.getHeader("X-B3-Sampled"),request.getHeader("X-Span-Name"));
		return restTemplate().getForEntity("http://trace-2/trace-2",String.class).getBody();
	}
@RequestMapping(value = "/trace-2",method = RequestMethod.GET)
	public String trace(HttpServletRequest request){
		logger.info("=========,TraceId={},SpanId={},ParentSpanId={},Sampled={},SpanName={}"
				,request.getHeader("X-B3-TraceId"),request.getHeader("X-B3-SpanId")
				,request.getHeader("X-B3-ParentSpanId"),request.getHeader("X-B3-Sampled"),request.getHeader("X-Span-Name"));
		return "Trace";
	}

2.在trace-1和trace-2的application.properties中加入配置,可以更加直观的观察跟踪信息

logging.level.org.springframework.web.servlet.DispatcherServlet=debug

重启trace-1和trace-2的应用,可以看到
trace-1的控制台
trace-1
trace-2的控制台
trace-2
trace-2中的ParentSpanId就是trace-1中的SpanId,trace-2中的TraceId与trace-1中的TraceId相同

与Logstash整合

由于日志文件都离散地存储在各个服务实例的文件系统之上,引入基于日志的分析系统是一个不错的选择,比如ELK平台,它可以轻松地收集和存储跟踪日志,同时还可以根据TraceId搜索出对应请求链路相关的明细日志
ELK平台主要由ElasticSearch、Logstash和Kibana三个工具组成

  • ElasticSearch是一个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,RESTful风格接口,多数据源,自动搜索负载等
  • Logstash是一个完全开源的工具,它可以对日志进行收集、过滤,并将其存储供以后使用
  • Kibana也是一个开源和免费的工具,可以为Logstash和ElasticSearch提供日志分析友好的Web界面,可以帮助汇总、分析和搜索重要数据日志
    如何实现面向Logstash的日志输出配置
  • 在pom.xml中新增依赖
<dependency>
	<groupId>net.logstash.logbackgroupId>
	<artifactId>logstash-logback-encoderartifactId>
	<version>4.6version>
dependency>
  • 在工程/resource目录下创建bootstrap.properties配置,并将spring.application.name=trace-1加到配置中
  • 在工程/resource目录下创建logback配置文件logback-spring.xml

与ZipKin整合

SPRING CLOUD微服务实战笔记--分布式服务跟踪:Spring Cloud Sleuth_第1张图片
Collector:收集器组件,它主要处理从外部系统发过来的跟踪信息,将这些信息转换为Zipkin内部处理的Span格式,以支持后续的存储、分析、展示等功能
Storage:存储组件,它主要处理收集器接收到的跟踪信息,默认会将这些信息存储在内存中。我们也可以修改此存储策略,通过使用其他存储组件将跟踪信息存储到数据库中。
RESTful API:API组件,它主要用来提供外部访问接口。比如给客户端展示跟踪信息,或是外接系统访问以实现监控等。
Web UI:UI组件,基于API组件实现的上层应用。通过UI组件,用户可以方便而又直观地查询和分析跟踪信息。

HTTP收集

第一步:搭建Zipkin Server

  • 创建一个基础的SpringBoot应用,命名为zipkin-server,并修改pom.xml引入依赖
<dependency>
	<groupId>io.zipkin.javagroupId>
	<artifactId>zipkin-serverartifactId>
	<version>2.0.1version>
dependency>

<dependency>
	<groupId>io.zipkin.javagroupId>
	<artifactId>zipkin-autoconfigure-uiartifactId>
	<version>2.0.1version>
dependency>
  • 创建应用主类,使用@EnableZipkinServer注解来启动ZipkinServer,具体如下:
@EnableZipkinServer
@SpringBootApplication
public class HelloApplication {
	public static void main(String[] args) {
		SpringApplication.run(HelloApplication.class, args);
	}
}
  • 修改application.properties中的配置
spring.application.name=zipkin-server
server.port=9411

创建完成后,启动工程,并访问http://localhost:9411/,可以看到如下Zipkin管理页面:
SPRING CLOUD微服务实战笔记--分布式服务跟踪:Spring Cloud Sleuth_第2张图片
第二步:为应用引入和配置Zipkin服务
在完成了Zipkin Server的搭建之后,还需要对应用做一些配置,以实现将跟踪信息输出到Zipkin Server。

  • 在trace-1和trace-2的pom.xml中引入spring-cloud-sleuth-zipkin依赖,具体如下所示:
<dependency>
	<groupId>org.springframework.cloudgroupId>
	<artifactId>spring-cloud-sleuth-zipkinartifactId>
	<version>1.3.5.RELEASEversion>
dependency>
  • 在trace-1和trace-2的application.properties中增加Zipkin Server的配置信息。
spring.zipkin.base-url=http://localhost:9411

重启eureka-server、trace-1、trace-2,发送请求http://localhost:9101/trace-1,可以查询出在日志中出现的跟踪信息
SPRING CLOUD微服务实战笔记--分布式服务跟踪:Spring Cloud Sleuth_第3张图片
单击每一条记录,还能看到具体的跟踪信息
SPRING CLOUD微服务实战笔记--分布式服务跟踪:Spring Cloud Sleuth_第4张图片

消息中间件收集

第一步:修改客户端trace-1 和trace-2

  • 修改客户端的pom.xml,引入
<dependency>
	<groupId>org.springframework.cloudgroupId>
	<artifactId>spring-cloud-starter-sleuthartifactId>
	<version>1.3.5.RELEASEversion>
dependency>


<dependency>
	<groupId>org.springframework.cloudgroupId>
	<artifactId>spring-cloud-sleuth-streamartifactId>
	<version>1.3.5.RELEASEversion>
dependency>
<dependency>
	<groupId>org.springframework.cloudgroupId>
	<artifactId>spring-cloud-starter-stream-rabbitartifactId>
	<version>1.3.4.RELEASEversion>
dependency>
  • application.properties配置修改,增加消息中间件的相关配置
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=springcloud
spring.rabbitmq.password=123456

第二步:修改zipkin-server服务端

<dependencies>
	<dependency>
		<groupId>io.zipkin.javagroupId>
		<artifactId>zipkin-serverartifactId>
		<version>2.0.1version>
	dependency>
	<dependency>
		<groupId>io.zipkin.javagroupId>
		<artifactId>zipkin-autoconfigure-uiartifactId>
		<version>2.0.1version>
	dependency>
	<dependency>
		<groupId>org.springframework.cloudgroupId>
		<artifactId>spring-cloud-sleuth-zipkin-streamartifactId>
		<version>1.3.5.RELEASEversion>
	dependency>
	<dependency>
		<groupId>org.springframework.cloudgroupId>
		<artifactId>spring-cloud-starter-stream-rabbitartifactId>
		<version>1.3.4.RELEASEversion>
	dependency>
dependencies>
  • 重新启动eureka-server、trace-1、trace-2、zipkin-server,同时确保RabbitMQ也处于运行状态,可以在RabbitMQ的管理页面看到一个名为sleuth的交换器
    通过向trace-1的接口发送几个请求http://localhost:9101/trace-1,可以看到管理页面中的统计曲线变化
    SPRING CLOUD微服务实战笔记--分布式服务跟踪:Spring Cloud Sleuth_第5张图片

数据存储

默认情况下,Zipkin Server会将跟踪信息存储在内存中,一方面每次重启Zipkin Server都会使之前收集的跟踪信息丢失,另一方面当有大量跟踪信息时内存存储会成为瓶颈,所以通常情况下都会将跟踪信息对接到外部存储组件中去,比如使用MySQL存储

你可能感兴趣的:(SpringCloud,SpringCloud)