分布式跟踪容许您跟踪分布式系统中的申请。本文通过理解如何应用 Spring Cloud Sleuth 和 Zipkin 来做到这一点。
对于一个做所有事件的大型应用程序(咱们通常将其称为单体应用程序),跟踪应用程序内的传入申请很容易。咱们能够跟踪日志,而后弄清楚申请是如何解决的。除了应用程序日志自身之外,咱们无需查看其余任何内容。
随着工夫的推移,单体应用程序变得难以扩大,难以解决大量申请以及随着代码库规模的不断扩大向客户提供新性能。这导致将单体架构合成为微服务,这有助于扩大单个组件并有助于更快地交付。
但并非所有闪耀的都是黄金,对吧?微服务也是如此。咱们将整个单体零碎拆分为微服务,由一组本地函数调用解决的每个申请当初都被调用一组分布式服务所取代。这样一来,咱们就失去了追踪在单体利用中很容易实现的申请之类的事件。当初,要跟踪每个申请,咱们必须查看每个服务的日志,并且很难关联。
因而,在分布式系统的状况下,分布式跟踪的概念有助于跟踪申请。
什么是分布式跟踪?
分布式跟踪是一种机制,咱们能够应用它跟踪整个分布式系统中的特定申请。它容许咱们跟踪申请如何从一个零碎停顿到另一个零碎,从而实现用户的申请。
分布式跟踪的要害概念
分布式跟踪蕴含两个次要概念:
- 跟踪 ID
- 跨度编号
跟踪 id 用于跟踪传入申请并在所有组合服务中跟踪它以满足申请。Span id 逾越服务调用以跟踪接管到的每个申请和收回的响应。
让咱们看一下图表。
传入的申请没有任何跟踪 ID。拦挡调用的第一个服务会生成跟踪 ID“ID1”及其跨度 ID“A”。span id“B”涵盖了从服务器一的客户端发出请求到服务器二接管、解决并收回响应的工夫。
带有 Spring Cloud Sleuth 的 Spring Boot 示例
让咱们创立一个集成了 Spring Cloud Sleuth 的应用程序。首先,让咱们拜访 https://start.spring.io/ 并应用依赖项“Spring Web”和“Spring Cloud Sleuth”创立一个应用程序。
当初让咱们创立一个带有两个申请映射的简略控制器。
public class Controller {private static final Logger logger = LoggerFactory.getLogger(Controller.class);
private RestTemplate restTemplate;
@Value("${spring.application.name}")
private String applicationName;
public Controller(RestTemplate restTemplate) {this.restTemplate = restTemplate;}
@GetMapping("/path1")
public ResponseEntity path1() {logger.info("Request at {} for request /path1", applicationName);
String response = restTemplate.getForObject("http://localhost:8090/service/path2", String.class);
return ResponseEntity.ok("response from /path1 +"+ response);
}
@GetMapping("/path2")
public ResponseEntity path2(){logger.info("Request at {} at /path2", applicationName);
return ResponseEntity.ok("response from /path2");
}
在这里,我创立了两条门路,Path1 调用 Path2 固定端口 8090。这里的想法是运行同一应用程序的两个独自实例。
当初为了容许侦探将标头注入到传出申请中,咱们须要将 RestTemplate 作为 bean 注入,而不是间接初始化它。这将容许侦探向 RestTemplate 增加一个拦截器,以将带有跟踪 id 和跨度 id 的标头注入到传出申请中。
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {return builder.build();
}
当初,让咱们启动两个实例。为此,首先,构建应用程序,mvn clean verify 而后运行以下命令来启动“服务 1”。
java -jar \target/Distributed-Service-0.0.1-SNAPSHOT.jar \--spring.application.name=Service-1 \--server.port=8080
而后在不同的终端上运行“服务 2”,如下所示:
java -jar \target/Distributed-Service-0.0.1-SNAPSHOT.jar \--spring.application.name=Service-2 \--server.port=8090
应用程序启动后,调用“Service 1”,/path1 如下所示:
curl -i http://localhost:8080/service/path1
当初让咱们看看“服务 1”的日志。
INFO [Service-1,222f3b00a283c75c,222f3b00a283c75c] 41114 --- [nio-8080-exec-1] c.a.p.distributedservice.Controller : Incoming request at Service-1 for request /path1
日志蕴含方括号,其中蕴含三个局部 [Service Name, Trace Id, Span Id]。对于第一个传入的申请,因为没有传入的 trace id,span id 与 trace id 雷同。
查看“服务 2”的日志,咱们看到咱们为此申请有一个新的 span id。
INFO [Service-2,222f3b00a283c75c,13194db963293a22] 41052 --- [nio-8090-exec-1] c.a.p.distributedservice.Controller : Incoming request at Service-2 at /path2
我截获了从“服务 1”发送到“服务 2”的申请,并发现传出的申请中曾经存在以下标头。
x-b3-traceid:"222f3b00a283c75c",
x-b3-spanid:"13194db963293a22",
x-b3-parentspanid:"222f3b00a283c75c
在这里,咱们看到下一个操作(对“服务 2”的调用)的跨度曾经注入到标头中。这些是在客户端发出请求时由“服务 1”注入的。这意味着下一次调用“服务 2”的跨度曾经从“服务 1”的客户端开始。在下面显示的题目中,“服务 1”的 span id 当初是下一个 span 的父 span id。
为了让事件更容易了解,咱们能够应用名为 Zipkin 的拦截器工具直观地查看跟踪。
应用 Zipkin 可视化跟踪
要将 Zipkin 与应用程序集成,咱们须要向应用程序增加 Zipkin 客户端依赖项。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
增加此依赖项后,Zipkin 客户端默认将跟踪发送到 Zipkin 服务器的 9411 端口。让咱们应用其 docker 映像启动 Zipkin 服务器。我为此创立了一个简略的 docker-compose 文件。
version: "3.1"
services:
zipkin:
image: openzipkin/zipkin:2
ports:
- "9411:9411"
咱们当初能够应用 docker-compose up 命令启动服务器。而后,您能够在以下地位拜访 UIhttp://localhost:9411/
因为咱们应用的是默认端口,咱们不须要指定任何属性,然而如果您打算应用不同的端口,则须要增加以下属性。
spring:
zipkin:
baseUrl: http://localhost:9411
实现后,让咱们应用下面雷同的命令启动两个应用程序。在向门路中的“服务 1”发出请求时,/path1 咱们会失去以下跟踪。
这里显示了两个服务的跨度。咱们能够通过查看跨度来更深刻地开掘。
“服务 1”的跨度是一个失常的跨度,涵盖了它接管到返回响应的申请。乏味的是第二个跨度。
在此,跨度中有四个点。
- 第一点是指来自“服务 1”的客户端何时开始申请。
- 第二点是“服务 2”开始解决申请的工夫。
- 第三点是“Server 1”上的客户端实现接管响应的工夫。
- 最初,“服务器 2”实现的最初一点。
因而,咱们理解了如何将分布式跟踪与 Spring Cloud Sleuth 集成,并应用 Zipkin 可视化跟踪。