如何使用Spring Boot和Zipkin实现分布式跟踪

x33g5p2x  于2022-09-18 转载在 Spring  
字(5.5k)|赞(0)|评价(0)|浏览(952)

在这篇文章中,我们将学习如何使用Zipkin和Spring Boot实现分布式跟踪。

Spring Boot目前是开发者创建微服务的首选。由于有多个服务,单个请求的追踪是非常麻烦的。为了更好的实现跟踪,让我们一起来学习Zipkin。

Zipkin架构

Zipkin是一个distributed tracing tool,有两个组件。一个是收集数据的应用服务,一个是报告给上述服务器的tracer(instrumentation) library。这个追踪器库在运行时与应用程序一起使用。下面是Zipkin官方网站上的一个简单架构图。


Zipkin分布式跟踪的架构

正如你在架构图中看到的,所有有Reporter的应用程序都与Zipkin收集器关联并提供信息。这些信息通常都遵循B3传播规范

B3传播和追踪规范

B3规范是一组HTTP头,用于将跟踪信息从一个应用程序传递给另一个。假设你有服务A调用服务B,服务B调用服务C,Zipkin使用这种格式在Spring Boot和Zipkin服务器之间转发跟踪信息。

在这种情况下。

  • 整个过程被称为一个事务。
  • 每个API调用都是一个跨度。技术上讲,一个操作单元就是span。但在这个例子中,每个API调用都是一个操作。
  • 伴随着这两个事实,在父跨度和子跨度之间也存在着关联。在这个例子中,对A进行的API调用触发了B的API,所以A是B的父级。

所有上述信息就是我们所说的跟踪信息或Trace Context。这个上下文应该从父代传到子代,这样每个应用程序的代理就可以获得这些信息,并将其转发给Zipkin中央服务器。

这里是B3头文件的来历。由于应用程序使用HTTP进行通信,所有这些信息都可以被编码为HTTP头信息并向下传递。

以下是B3头信息的列表。

Zipkin TraceId

对于每笔交易,Zipkin启动器都会生成一个独特的TraceId,以十六进制编码。这个头文件的密钥是X-B3-TraceId。这个值在交易的整个过程中不会改变。

SpanId

SpanId是一个64位的十六进制值,表示当前的操作/API调用。它的头键是X-B3-SpanId

ParentSpanId

每个API调用可能有也可能没有对其他服务的后续调用。如果它们确实有后续调用,那么我们可以将所有这些API调用形成一棵树。这种情况就是X-B3-ParentSpanId头出现的地方。父级跨度标识是父级API调用或操作的跨度标识。当Zipkin服务器从所有服务器获得所有的跟踪上下文时,它可以使用ParentSpanIds来安排跟踪树结构。

Sampling State

标头X-B3-Sampled在是否追踪后续跨度上取1或0。这个决定默认是以0.1的概率(10%)做出的。这意味着在根跨度处,跟踪报告者可能会也可能不会根据一个随机的概率来创建一个上下文。我们可以用三种方式强迫报告者进行采样。* 用X-B3-Sampled调用根跨度为1 * 将默认的追踪概率设置为1(100%)。我们将在后面讨论这个问题。* 使用Debug标志进行调用。

Debug Flag

X-B3-Flags: 1是DEBUG标志的表示。如果没有这个头的任何其他值,就意味着跟踪不是在调试模式下。另外,在调试模式下,跟踪决定的概率是1(总是跟踪。) 这个标头在生产中很有帮助,因为你想确保Zipkin会追踪该事务。

因此,一个典型的中间跨度的头文件集看起来像下面这样。

X-B3-TraceId: 98dcb578d3c0dec17f57a9950b28bcd0
X-B3-ParentSpanId: cf6cf79caba2eb97
X-B3-SpanId: ee802197f3d49d5f
X-B3-Sampled: 1
Code language: HTTP (http)

说完了技术性的东西。让我们尝试一下简单的Zipkin设置。

设置Zipkin服务器

服务器的设置很简单。Zipkin服务器是一个可执行的Jar,可以直接downloaded from Maven repository。如果你使用的是Linux,你可以运行以下命令下载并启动Zipkin服务器。

$ curl -sSL https://zipkin.io/quickstart.sh | bash -s
$ java -jar zipkin.jar
Code language: JavaScript (javascript)

也有一个docker镜像可用于快速启动。

docker run -d -p 9411:9411 openzipkin/zipkin

一旦你启动jar或docker镜像,应用程序用户界面将在http://localhost:9411出现。


Zipkin UI

Spring Boot Zipkin的依赖性

Zipkin有一个Spring Boot启动器,是Spring Cloud生态系统的一部分。而他们的所有依赖项都在spring-cloud-dependencies pom中管理。要把Zipkin添加到你的项目中,你需要把spring-cloud-dependencies作为管理的依赖项。

所以首先添加以下dependencyManagement片段。如果你已经设置了依赖管理,只需在适当位置添加dependency标签。

<dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Hoxton.SR8</version>  
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
Code language: HTML, XML (xml)

接下来,你需要引入Zipkin的spring boot启动器。这一步和添加其他启动器一样简单。只需在依赖项列表中加入以下组件。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
Code language: HTML, XML (xml)

最重要的是,你可以通过选择Zipkin作为依赖,从start.spring.io生成同样的设置。

Zipkin在Spring Boot中执行

同时,我们创建了一个/hello Rest API端点,调用属性文件中配置的URL。

@RestController
@SpringBootApplication
public class ZipkinDemoApplication {

    @Autowired
    private RestTemplate restTemplate;

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Value("${spring.application.name}")
    private String appName;

    @Value("${target.service.url}")
    private String target;

    @GetMapping("/hello")
    public String sayHello() {
        return appName + " to > "
                + restTemplate.getForObject(target, String.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(ZipkinDemoApplication.class, args);
    }

}
Code language: JavaScript (javascript)

这里的想法是,只要改变server.porttarget.service.url,我就可以模拟一个服务调用另一个服务。

将Spring Boot指向Zipkin服务器

这里要做的第一件事是将我们的Spring Boot应用程序指向Zipkin服务器。同时,我们还必须指定收集器如何与中央Zipkin服务器通信。(目前的选项是ActiveMQ、RABBIT、KAFKA和WEB)。我们将使用WEB发送器类型。为了确保Zipkin Reporter对所有请求进行采样,我把采样器的概率设置为1。
在生产中千万不要把概率设置为1,原因有二。请确保你考虑到这些要点。 

  1. 你的应用程序会变得很慢。
  2. Zipkin中央服务器将获得巨大的负载。

我已在**应用程序属性中指定了这些设置。

spring.zipkin.base-url=http://localhost:9411/
spring.zipkin.sender.type=web
spring.sleuth.sampler.probability=1.0
Code language: JavaScript (javascript)

除了这些,我们还将把服务器端口、目标URL和应用程序名称作为spring boot命令行参数传递。这种方法使我们可以灵活地在不同的端口下启动任意多的服务。

下面这组命令建立了项目,并以不同的端口和终端URL启动了三个演示应用程序的实例。

mvn clean install
java -jar -Dspring.application.name=APP-1 -Dserver.port=8001 \
          -Dtarget.service.url=http://localhost:8002/hello   \
          zipkin-demo-0.0.1-SNAPSHOT.jar
java -jar -Dspring.application.name=APP-2 -Dserver.port=8002 \
          -Dtarget.service.url=http://localhost:8003/hello   \
          zipkin-demo-0.0.1-SNAPSHOT.jar
java -jar -Dspring.application.name=APP-3 -Dserver.port=8003 \
          -Dtarget.service.url=https://run.mocky.io/v3/ed8d9ae2-7d0d-4411-8b8c-66106d8a2721 \
          zipkin-demo-0.0.1-SNAPSHOT.jar
Code language: JavaScript (javascript)

如果我们设置正确,我们将有三个应用程序,以这样的方式,APP-1调用APP-2,APP-2调用APP3,APP-3调用mock服务。

我们来调用APP-1的API。

$curl http://localhost:8001/hello -iv

GET http://localhost:8001/hello

HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 54
Date: Wed, 04 Nov 2020 15:36:04 GMT
Keep-Alive: timeout=60
Connection: keep-alive

APP-1 to > APP-2 to > APP-3 to > {
  "status" : "OK"
}
Code language: PHP (php)

输出告诉我们从APP-2APP-2APP-3等的流程。这个输出证明了我们的设置是有效的。让我们检查一下Zipkin服务器的用户界面。

Zipkin用户界面

选择RunQuery -> Expand All,你会发现有一个新的跟踪。如你所见,这个跟踪是为发生在app-1、app-2和app-3之间的交易创建的。通过点击显示该条目,我们可以看到调用是如何发生的,以及每个后续的跨度花了多少时间。通过选择每个跨度,我们甚至可以看到跨度标识和父标识。


Zipkin UI显示Spring Boot应用程序的跟踪信息

所有这些看起来很神奇。但在引擎盖下,Zipkin启动器正在拦截每个应用程序的所有请求,并添加这些span ID和事务ID。同时,报告者将把这些信息发送到Zipkin中央服务器。

让我们看一下日志。如果你看到我之前提到的属性文件,我已经将日志级别标记为debug。这种设置会让我们看到每个请求的HTTP头信息。

突出显示的部分显示了APP-1正在向APP-2发送哪些头信息。请自行尝试使用这些头信息。

Zipkin启动器最棒的地方是不需要改变额外的代码,也没有副作用。UI中的跨度给出了一个标签列表,将携带有关控制器方法、方法类型等信息。

总结

综上所述,我们学会了如何为Spring Boot应用程序添加Zipkin分布式跟踪,在多个微服务之间进行跟踪。

相关文章