100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 《深入理解 Spring Cloud 与微服务构建》第十四章 服务链路追踪 Spring Cloud Sleuth

《深入理解 Spring Cloud 与微服务构建》第十四章 服务链路追踪 Spring Cloud Sleuth

时间:2023-11-19 15:24:39

相关推荐

《深入理解 Spring Cloud 与微服务构建》第十四章 服务链路追踪 Spring Cloud Sleuth

《深入理解 Spring Cloud 与微服务构建》第十四章 服务链路追踪 Spring Cloud Sleuth

文章目录

《深入理解 Spring Cloud 与微服务构建》第十四章 服务链路追踪 Spring Cloud Sleuth一、为什么需要 Spring Cloud Sleuth二、基本术语三、案例讲解1.启动 Zipkin Server2.构建服务提供者3.构建服务消费者4.项目演示四、在链路数据中添加自定义数据五、使用 RabbitMQ 传输链路数据六、在 MySQL 数据库中存储链路数据七、在 ElasticSearch 中存储链路数据

一、为什么需要 Spring Cloud Sleuth

微服务架构是一个分布式架构,微服务系统按业务划分服务单元,一个微服务系统往往有很多个服务单元。由于服务单元数量众多,业务的复杂性较高,如果出现了错误和异常,很难去定位。主要体现在一个请求可能需要调用很多个服务,而内部服务的调用复杂性决定了问题难以定位。所以在微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与,参与的顺序又是怎样的,从而达到每个请求的步骤清晰可见,出现问题能够快速定位的目的

Google 开源了 Dapper 链路追踪组件,并在 年发表了论文《Dapper,a Large-Scale Distributed Systems Tracing Infrastructure》,这篇论文是业内实现链路追踪的标杆和理论基础,具有很高的参考价值

目前,常见的链路追踪组件有 Google 的 Dapper、Twitter 的 Zipkin,以及阿里的 Eagleeye(鹰眼)等,它们都是非常优秀的链路追踪开源组件

本章主要讲述如何在 Spring Cloud Sleuth 中集成 Zipkin。在 Spring Cloud Sleuth 中集成 Zipkin 非常简单,只需要引入相应的依赖并做相关的配置即可

二、基本术语

Spring Cloud Sleuth 采用了 Google 的开源项目 Dapper 的专业术语

Span

基本工作单元,发送一个远程调度任务就会产生一个 Span,Span 是用一个 64 位 ID 唯一标识的,Trace 是用另一个 64 位 ID 唯一标识的。Span 还包含了其它的信息,例如摘要、时间戳时间、Span 的 ID 以及进程 ID

Trace

由一系列 Span 组成的,呈树状结构。请求一个微服务系统的 API 接口,这个API 接口需要调用多个微服务单元,调用每个微服务单元都会产生一个新的 Span,所有由这个请求产生的 Span 组成了这个 Trace

Annotation

用于记录一个事件,一些核心注解用于定义一个请求的开始和结束,这些注解如下:

cs-Client Sent:客户端发送一个请求,这个注解描述了 Span 的开始sr-Server Received:服务端获得请求并准备开始处理它,如果将其 sr 减去 cs 时间戳,便可得到网络传输的时间ss-Server Sent:服务端发送响应,该注解表明请求处理的完成(当请求返回客户端),用 ss 的时间戳减去 sr 时间戳,便可以得到服务器请求的时间cr-Client Received:客户端接收响应,此时 Span 结束,用 cr 的时间戳减去 cs 时间戳,便可以得到整个请求所消耗的时间

Spring Cloud Sleuth 提供了一套完整的链路解决方案,它可以结合 Zipkin,将链路数据发送到 Zipkin,并利用 Zipkin 来存储链路信息,也可以利用 Zipkin UI 来展示数据

那么什么是 Zipkin 呢?

Zipkin 是 Twitter 的一个开源项目,它基于 Google 的 Dapper 实现,被业界广泛使用。Zipkin 致力于收集分布式系统的链路数据,提供了数据持久化策略,也提供面向开发者的 API 接口,用于查询数据,还提供了 UI 组件帮助我们查看具体的链路信息

Zipkin 提供了可插拔式的数据存储方式,目前支持的数据存储由 In-Memory、MySQL、Cassandra 和 ElasticSearch

Zipkin 的架构如图所示:

它主要由 4 个核心组件构成:

Collector:链路数据收集器,主要用于处理从链路客户端发送过来的链路数据,将这些数据转换为 Zipkin 内部处理的 Span 格式,以支持后续的存储、分析和展示等功能Storage:存储组件,用来存储接收到的链路数据,默认会将这些数据存储在内存中,同时支持多种存储策略,比如将链路数据存储在 M有SQL、Cassandra 和 ElasticSearch 中RESTful API:API 组件,它是面向开发者的,提供外部访问 API 接口,可以通过这些 API 接口来自定义展示界面WEB UI:UI 组件,基于 API 接口实现的上层应用,用户利用 UI 组件可以很方便地查询和分析链路数据

三、案例讲解

本章的案例一共有 4 个工程,基本信息如表所示:

和之前的工程一样,采用 Maven 工程的多 Module 形式。新建一个主 Maven 工程,在主 Maven 工程的 pom 文件里指定 Spring Boot 的版本为 2.1.0,Spring Cloud 版本为 Greenwich。RELEASE。eureka-server 工程作为服务注册中心,zipkin-server 作为链路追踪服务中心,负责存储来南路数据,Spring Cloud 从 Edgware 版本开始,使用 Jar 包的形式启动,Jar 包需要从官方下载,用户不需要额外创建工程。eureka-client 工程是一个服务提供者,对外暴露 API 接口,同时它也作为链路追踪客户端,产生链路数据,并将链路数据传递给服务消费者。eureka-feign-client 作为服务消费者,调用 eureka-client 提供的服务,同时它也作为链路追踪客户端,产生链路数据,并将链路数据上传给链路追踪服务中心 zipkin-server

1.启动 Zipkin Server

eureka-server 的构建流程就不再一一演示,直接启动 Zipkin Server

在 Spring Cloud Dalston 版本中,zipkin-server 可以通过引入相关依赖的方式构建工程。从 Edgware 版本之后,这一方式改变为强制采用官方提供的 Jar 包的形式启动。因此用户需要下载官方提供的 Jar 包,使用 Git Bash 通过以下命令一键启动

curl -sSl https://zipkin.io/quickstart.sh | bash -sjava -jar zipkin.jar

上述第一行命令会从 Zipkin 官网下载官方的 Jar 包,第二行命令用来运行 zipkin.jar 文件

通过 java -jar zipkin.jar 的方式启动之后,在浏览器上访问 localhost:9411,显示的界面如图所示:

2.构建服务提供者

在主 Maven 工程下建一个 Module 工程,取名为 eureka-client,作为服务提供者,对外暴露 API 接口。user-service 工程的 pom 文件继承了主 Maven 工程的 pom 文件,并引入了 Eureka 的起步依赖 spring-cloud-starter-netflix-eureka-client、WEB 起步依赖 spring-boot-starter-web,以及 Zipkin 的起步依赖 spring-cloud-starter-zipkin 和 sleuth 的起步依赖 spring-cloud-starter-sleuth,代码如下:

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-sleuth</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-zipkin</artifactId></dependency></dependencies>

在程序的配置文件 application.yml 中,指定程序名为 eureka-client,端口号为 8763,服务注册地址 http://localhost:8761/eureka/,Zipkin Server 地址为 http://localhost:9411。spring-sleuth.sample.percentage 为 1.0,即以 100% 的概率将链路的数据上传给 Zipkin Server,在默认情况下,该值为 0.1。另外,需要通过配置 spring.sleuth.web.client.enable 为 “true” 来开启 Sleuth 的功能,具体配置文件代码如下:

server:port: 8763spring:application:name: eureka-clientsleuth:web:client:enabled: truesampler:probability: 1.0 #设置采样比例,1.0 即全部都需要,默认为 0.1zipkin:base-url: http://localhost:9411/ #指定 zipkin 服务器地址eureka:client:service-url: defaultZone: http://lcoalhost:8761/eureka/

在 HiController 类建一个 “/hi” 的 API 接口,对外提供服务,代码如下:

package com.sisyphus.controller;import brave.Tracer;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HiController {@AutowiredTracer tracer;@Value("${server.port}")String port;public String home(@RequestParam String name){tracer.currentSpan().tag("name","sisyphus");return "hi " + name + ",i am from port:" + port;}}

最后作为 Eureka Client,需要在程序的启动类 EurekaClientApplication 加上 @EnableEurekaClient 注解,开启 Eureka Client 的功能

3.构建服务消费者

新建一个名为 eureka-feign-client 的工程,这个工程作为服务消费者,使用 FeignClient 来消费服务;同时作为 Zipkin 客户端,将链路数据上传给 Zipkin Server。在工程的 pom 文件中除了需要继承主 Maven 工程的 pom 文件以外,还需引入如下的依赖,代码如下:

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-sleuth</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-zipkin</artifactId></dependency></dependencies>

在工程的配置文件 application.yml 中,配置程序名为 eureka-feign-client,端口号为 8765,服务注册地址为 http://localhost:8761/eureka/。另外,设置 spring.sleuth.web.client.enable 为 “true” 来使 WEB 开启 Sleuth 功能;spring.sleuth.sampler.probability 可以被设置为小数;最大值为 1.0,标识链路数据 100% 收集到 zipkin-server;当设置为 0.1 时,表示以 10% 的概率收集链路数据;spring.zipkin.base-url 设置 zipkin-server 的地址。配置代码如下:

server:port: 8765spring:application:name: eureka-feign-clientsleuth:web:client:enabled: truesampler:probability: 1.0zipkin:base-url: http://localhost:9411/ #指定 Zipkin 服务器的地址eureka:client:service-url:defaultZone: http://localhost:8761/eureka/

在程序的启动类 EurekaFeignClientApplication 上加 @EnableEurekaClient 和 @EnableFeignClients 注解,代码如下:

package com.sisyphus;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.flix.eureka.EnableEurekaClient;import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication@EnableEurekaClient@EnableFeignClientspublic class EurekaFeignClientApplication {public static void main(String[] args) {SpringApplication.run(EurekaFeignClientApplication.class, args);}}

服务消费者通过 FeignClient 消费服务提供者提供的服务,FeignClient 的代码如下:

package com.sisyphus.feign;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;@FeignClient("eureka-client")@Componentpublic interface EurekaClientFeign {@GetMapping("/hi")String sayHiFromClientEureka(@RequestParam("name") String name);}

在 Service 层,通过调用 EurekaClientFeign 来消费服务,代码如下:

package com.sisyphus.service;import com.sisyphus.feign.EurekaClientFeign;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class HiService {@AutowiredEurekaClientFeign eurekaClientFeign;public String sayHi(String name){return eurekaClientFeign.sayHiFromClientEureka(name);}}

在 Controller 层,对外暴露一个 API 接口,通过 HiService 来调用 eureka-client 的服务,代码如下:

package com.sisyphus.controller;import com.sisyphus.service.HiService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HiController {@AutowiredHiService hiService;@GetMapping("/hi")public String sayHi(@RequestParam(defaultValue = "sisyphus", required = false) String name){return hiService.sayHi(name);}}

4.项目演示

完整的项目搭建完毕,依次启动 eureka-server、eureka-client 和 eureka-feign-client,并启动 zipkin.jar。在浏览器上访问 http://localhost:8765/hi(如果报错,是因为服务与发现需要一定的时间,需要耐心等待几十秒),浏览器显示如下:

hi sisyphus,i am from port:8763

访问 http://localhost:9411,即访问 Zipkin 的展示界面,如图所示:

这个界面用于展示 Zipkin Server 收集的链路数据,可以根据服务名、开始时间、结束时间、请求消耗的时间等条件来查找:

从图中可知请求的调用情况,例如请求的调用时间、消耗时间,以及请求调用的链路情况等

单击导航栏的 “依赖” 按钮,可以查看服务的依赖关系。在本案例中,eureka-feign-client 消费了 eureka-client 提供的 API 服务,这两个服务的依赖关系如图所示:

四、在链路数据中添加自定义数据

现在需要实现这样一个功能:在链路数据中加上请求的操作人。本案例在 eureka-client 服务中实现。在 API 接口逻辑方法中,通过 Tracer 的 currentSpan 方法获取当前的链路数据的 Span,通过 tag 方法加上自定义的数据。在本案例中加上链路的操作人的代码如下:

package com.sisyphus.controller;import brave.Tracer;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HiController {@AutowiredTracer tracer;@Value("${server.port}")String port;@GetMapping("/hi")public String home(@RequestParam String name){tracer.currentSpan().tag("name","sisyphus");return "hi " + name + ",i am from port:" + port;}}

五、使用 RabbitMQ 传输链路数据

在上述案例中,最终 eureka-feign-client 收集的数据是通过 HTTP 上传给 zipkin-server 的。在 Spring Cloud Sleuth 中支持消息组件来传输链路数据,本节使用 RabbitMQ 来传输链路数据。使用 RabbitMQ 前需要安装 RaabitMQ 程序

链路数据是通过 RabbitMQ 来传输的,那么 zipkin-server 是如何直到 RabbitMQ 的地址,又如何监听收到的链路数据呢?zipkin-server 是通过读取环境变量来获取 RabbitMQ 的配置信息的,这需要在程序启动时通过环境变量的形式将配置信息注入环境中, 然后 zipkin-server 从环境变量中读取,可配置的属性如下表所示

比如用以下命令启动 Zipkin 的服务:

RABBIT_ADDRESSES=localhost java -jar zipkin.jar

上述命令等同于以下命令

java -jar zipkin.jar --zipkin.collector.rabbitmq.addressed=localhost

用上述两条命令中的任何一条重新启动 Zipkin 服务。接着改造 Zipkin Client(包括 gateway-service 工程和 user-service 工程),在它们的 pom 文件中加上 spring-cloud-stream-binder-rabbit 的依赖,代码如下:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-stream-binder-rabbit</artifactId></dependency>

同时在配置文件 application.yml 中加上 RabbitMQ 的配置,并去掉 spring.zipkin.base-url 的配置,代码如下;

spring:rabbitmq:host: loaclhostusername: guestpassword: guestport: 5672

这样就可以将链路的上传数据方式从 HTTP 改为消息代理组件 RabbitMQ

六、在 MySQL 数据库中存储链路数据

在上面的例子中,Zipkin Server 将数据存储在内存中,一旦程序重启,之前的链路数据会全部丢失,那么怎么将链路数据存储起来呢?Zipkin 支持将链路数据存储在 MySQL、ElasticSearch 和 Cassandra 数据库中

首先需要初始化 Zipkin 存储在 MySQL 数据的脚本,具体可到 GitHub 中搜索 Zipkin 进行查看。本案例中的源码资源文件夹中也包含该数据库脚本

在数据库中初始化上述脚本后,需要让 Zipkin 连接上数据库。Zipkin 连接数据库同连接 RabbitMQ 一样,都是从环境变量中读取的。Zipkin 连接数据库的属性所对应的环境变量如下表所示:

使用以下命令启动,Zipkin 就可以启动时连接数据库,并将链路数据存储在数据库中,命令如下:

STORAGE_TYPE=mysql MYSQL_HOST=localhost MYSQL_TCP_PORT=3306 MYSQL_USER=root MYSQL_PASS=123456 MYSQL_DB=zipkin java -jar zipkin.jar

上述命令等同于以下命令:

java -jar zipkin.jar --zipkin.torage.type=mysql --zipkin.torage.mysql.host=localhost --zipkin.torage.mysql.port=3306 --zipkin.torage.mysql.username=root -- zipkin.torage.mysql.password=123456

使用上面的命令启动 zipkin.jar 工程,然后在浏览器上访问 http://localhost:8765/hi,再访问 http://localhost:9411/zipkin/,就可以看到链路数据。这时去数据库查看数据,也可以看到存储再数据库的链路数据

这时重启应用 zipkin.jar,再次在浏览器上访问 httpL//localhost:9411/zipkin/,仍然可以得到之前的结果,证明链路数据存储在数据库中,而不是内存中

七、在 ElasticSearch 中存储链路数据

在高并发的情况下,使用 MySQL 存储链路数据显然不合理,这时可以选择使用 ElasticSearch 存储。读者需要自行安装 ElasticSearch 和 Kibana,下载地址为 http://www.elastic.co/products/elasticsearch。安装完成后启动,其中 ElasticSearch 的默认端口号为 9200,Kibana 的默认端口号为 5601

同理,Zipkin 连接 ElasticSearch 也是从环境变量中读取的连接属性,ElasticSearch 香瓜的呢环境变量和对应的属性如下表所示:

使用以下命令启动 Zipkin,Zipkin 就将链路数据存储在 ElasticSearch 中,启动命令如下:

STORAGE_TYPE=elasticsearch ES_HOSTS=http://localhost:9200 ES_INDEX=zipkin java -jar zipkin.jar

启动完成后,在浏览器上访问 httpL//localhost:8765/hi,再访问 http://localhost:9411/zipkin/,可以看到链路数据,这时链路数据存储在 ElasticSearch 中

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。