首先准备工作如下:
1.服务注册中心:eureka-server。
2.微服务应用:trace-1,实现REST接口,并调用trace-2应用的接口。
其pom.xml文件如下:
4.0.0
com.example
trace-1
0.0.1-SNAPSHOT
jar
trace-1
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
2.0.5.RELEASE
UTF-8
UTF-8
1.8
Finchley.SR1
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
应用主类如下:
@RestController
@EnableDiscoveryClient
@SpringBootApplication
public class Trace1Application {
private final Logger logger=Logger.getLogger(getClass());
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
@GetMapping("/trace-1")
public String trace(){
logger.info("--------------------------- trace1 ----------------------------------");
return restTemplate().getForEntity("http://trace-2/trace-2",String.class).getBody();
}
public static void main(String[] args) {
SpringApplication.run(Trace1Application.class, args);
}
}
application.properties文件如下:
spring.application.name=trace-1
server.port=9101
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
3.创建第二个微服务应用:trace-2,实现REST接口/trace-2。
其pom.xml跟trace-1的pom文件相同。
应用主类如下:
@RestController
@EnableDiscoveryClient
@SpringBootApplication
public class Trace2Application {
private final Logger logger=Logger.getLogger(getClass());
@GetMapping("/trace-2")
public String trace(){
logger.info("--------------------------- trace2 ----------------------------------");
return "Trace2";
}
public static void main(String[] args) {
SpringApplication.run(Trace2Application.class, args);
}
}
application.properties文件如下:
spring.application.name=trace-2
server.port=9102
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
启动三个服务后调用http://localhost:9101/trace-1
首先在trace-1和trace-2的pom.xml文件中加入spring-cloud-starter-sleuth依赖:
org.springframework.cloud
spring-cloud-starter-sleuth
然后访问http://localhost:9101/trace-1
第一个值:trace-1,它记录了应用的名称(application.properties中的spring.application.name参数配置的属性。)
第二个值:13a031b537091ddc,Spring Cloud Sleuth生成的一个ID,称为Trace ID,它用来标示一条请求链路。一条请求链路中包含一个Trace ID,多个Span ID。
第三个值:13a031b537091ddc,Spring Cloud Sleuth生成的另外一个ID,称为Span ID,他表示一个基本的工作单元,比如发送一个HTTP请求。
第四个值:false,表示是否要将该信息输出到Zipkin等服务中来收集和展示。
上面的Trace ID和Span ID是Spring Cloud Sleuth实现分布式服务跟踪的核心。在一次服务请求链路的调用中,会保持传递同一个Trace ID,从而将整个分布于不同微服务进程中的请求跟踪信息串联在一起。
分布式系统中的服务跟踪在理论上实现不复杂,它主要有两个关键点:
1.为了实现请求跟踪,当请求发送到分布式系统的入口端点时,只需要服务跟踪框架为该请求创建一个唯一的跟踪标识,同时在分布式系统内部流转的时候,框架始终保持传递该唯一标识,直到返回给请求方为止,这个唯一标识就是前文中提到的 TraceID
。通过 TraceID
的记录,我们就能将所有请求过程日志关联起来。
2.为了统计各处理单元的时间延迟,当请求达到各个服务组件时,或是处理逻辑到达某个状态时,也通过一个唯一标识来标记它的开始、具体过程以及结束,该标识就是我们前文中提到的Span ID,对于每个Span来说,它必须有开始和结束两个节点,通过记录开始Span和结束Span的时间戳,就能统计出该Span的时间延迟,除了时间戳记录之外,它还可以包含一些其他元数据,比如:事件名称、请求信息等。
在Spring Boot应用中,通过在工程中引入spring-cloud-starter-sleuth依赖之后,它会自动为当前应用构建起各通信通道的跟踪机制,如:
1.通过RabbitMQ,Kafka传递请求
2.通过Zuul代理传递的请求
3.通过RestTemplate发起的请求
查看源码可以获取:
X-B3-TraceId:一条请求链路(Trace)的唯一标识,必须值
X-B3-SpanId:一个工作单元(Span)的唯一标识,必须值
X-B3-ParentSpanId::标识当前工作单元所属的上一个工作单元,Root Span(请求链路的第一个工作单元)的该值为空
X-B3-Sampled:是否被抽样输出的标志,1表示需要被输出,0表示不需要被输出
X-Span-Name:工作单元的名称
修改trace-2的实现:
@RestController
@EnableDiscoveryClient
@SpringBootApplication
public class Trace2Application {
private final Logger logger = Logger.getLogger(getClass());
@GetMapping("/trace-2")
public String trace(HttpServletRequest request) {
logger.info("--------------------------- trace2 ----------------------------------");
logger.infof(" \n TraceId={} \n SpanId={} \n ParentSpanId={} \n Sampled={} \n Name={}"
, 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 "Trace2";
}
public static void main(String[] args) {
SpringApplication.run(Trace2Application.class, args);
}
}
为了更直观的观察跟踪信息,可以在application.properties增加如下配置:
logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG
参考《Spring Cloud微服务实战》