概述
引入
在微服务架构下,客户端一个请求不再仅仅通过一个后端服务和数据库服务就能返回响应,而是通过多个服务节点的协同调用才会产生最终的响应。因此,一个简单的请求就可能会形成一个复杂的分布式服务调用链路,链路中的任何一个服务出错或调用时间过长都会引起整个请求最后的失败。
当服务失败之后,我们就要快速定位具体是哪个服务节点发生了错误,但是我们使用人工一个一个排查就会造成巨大的人力浪费。并且我们还深知,写新需求不复杂,找错误、修bug才最难搞。
基于上述情况,微服务就研发了一套组件来解决上述问题,即分布式链路追踪。所谓的分布式链路追踪,就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示。比如请求经过各个服务节点的耗时,请求具体到哪台机器上,每个服务节点的请求状态等。
简单来说,分布式链路追踪就是把一个请求经过的所有节点都记录下来,包括但不限于耗时,父亲节点,节点状态等等。
Micrometer
Micrometer就是分布式链路追踪的落地组件,SpringCloudSleuth是分布式链路追踪的上一代组件。不过由于现在使用的是SpringBoot3以上的版本,因此与Sleuth不再兼容,该篇文章中,主要学的也是Micrometer组件。并且与zipkin相结合使用,下图所示,Micrometer将一次请求经过的路径进行记录,然后交给zipkin进行展现。
原理
一个请求通过一个TraceId来唯一标识,对于每个服务节点来说,都使用SpanId来标识,各个服务节点还会记录一个ParentId。最后通过TraceId、SpanId和ParentId将整条链路进行追踪。
其他组件
Cat:由大众点评开源,基于Java开发的实时应用监控平台,包括实时应用监控、业务监控,集成方案是通过代码埋点的方式来实现监控。比如:拦截器、过滤器等。对代码的侵入性很大、集成成本较高、风险很大。
Zipkin:由Twitter公司开源,开发源代码分布式的跟踪系统,用于收集服务的定时数据,以解决微服务架构中的延迟问题,包括:数据的收集、存储查找和展现,结合SpringCloudSleuth使用较为简单,集成方便,但是功能较简单。
Pinpoint:是一款开源的基于字节码注入的调用链分析,以及应用监控分析工具,特点是支持多种插件,UI功能强大,接入端无代码侵入。
Skywalking:是中国人开源的基于字节码注入的调用链分析,以及应用监控分析工具,特点是支持多种插件,UI功能较强,接入端无代码侵入。
ZipKin
Zipkin是一种分布式链路跟踪系统图形化的工具,Zipkin 是 Twitter 开源的分布式跟踪系统,能够收集微服务运行过程中的实时调用链路信息,并能够将这些调用链路信息展示到Web图形化界面上供开发人员分析,开发人员能够从ZipKin中分析出调用链路中的性能瓶颈,识别出存在问题的应用程序,进而定位问题和解决问题。
简单来说,ZipKin就是用来展示分布式链路追踪的结果,使得开发人员能够轻松便捷的找到问题出现的原因,然后着手解决。
使用官网进行下载:
下载成功之后,直接在其对应页面打开cmd,然后使用java -jar 文件名就可以运行了:
启动之后,进入127.0.0.1:9411/zipkin/看到如下画面,就算启动成功了。
案例代码
在该案例中,共创建了四个服务,链路代码是服务A调用服务B,服务B可以调用服务C和服务D。采用的技术栈有:Nacos、OpenFeign、LoadBalancer以及今天的主角Micrometer和ZipKin。
父工程中引入依赖
<!--链路追踪-->
<micrometer-tracing.version>1.2.0</micrometer-tracing.version>
<micrometer-observation.version>1.12.0</micrometer-observation.version>
<feign-micrometer.version>12.5</feign-micrometer.version>
<zipkin-reporter-brave.version>2.17.0</zipkin-reporter-brave.version>
<!--链路追踪-->
<!--micrometer-tracing-bom导入链路追踪版本中心 1-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bom</artifactId>
<version>${micrometer-tracing.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--micrometer-tracing指标追踪 2-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
<version>${micrometer-tracing.version}</version>
</dependency>
<!--micrometer-tracing-bridge-brave适配zipkin的桥接包 3-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
<version>${micrometer-tracing.version}</version>
</dependency>
<!--micrometer-observation 4-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation</artifactId>
<version>${micrometer-observation.version}</version>
</dependency>
<!--feign-micrometer 5-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-micrometer</artifactId>
<version>${feign-micrometer.version}</version>
</dependency>
<!--zipkin-reporter-brave 6-->
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
<version>${zipkin-reporter-brave.version}</version>
</dependency>
创建模块
由于这几个模块只用于测试分布式链路追踪,所以四个模块中内容几乎相同。
引入pom文件
四个模块的pom文件几乎相同,只有GAV不太相同,所以我只引入一个,剩下的大家稍稍修改一下就好了。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.wbz</groupId>
<artifactId>spring-cloud-test</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>cloud-micrometer-two-8602</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--注册中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--基础模块-->
<dependency>
<groupId>com.wbz</groupId>
<artifactId>cloud-commons</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!--链路追踪-->
<!--micrometer-tracing指标追踪 1-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
</dependency>
<!--micrometer-tracing-bridge-brave适配zipkin的桥接包 2-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<!--micrometer-observation 3-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation</artifactId>
</dependency>
<!--feign-micrometer 4-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-micrometer</artifactId>
</dependency>
<!--zipkin-reporter-brave 5-->
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
写yml文件
同样,yml文件中也是端口号和服务名不一样,大家自己修改一下就好。
server:
port: 8602
spring:
application:
name: cloud-micrometer-two-8602
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
service: ${spring.application.name}
profiles:
active: dev
# ========================zipkin===================
management:
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans
tracing:
sampling:
probability: 1.0 #采样率默认为0.1(0.1就是10次只能有一次被记录下来),值越大收集越及时。
写启动类
在启动类中,服务1和服务2都要远程调用,所以要加入开启远程调用的注解。当然,服务三和服务四也可以开启,不过要注意服务三和服务四是否有OpenFeign的依赖。
@EnableFeignClients // 远程调用
@SpringBootApplication
public class MicrometerCloudApplication8602 {
public static void main(String[] args) {
SpringApplication.run(MicrometerCloudApplication8602.class, args);
}
}
写业务类和远程调用的接口
服务三和服务四
@RestController
@RequestMapping("/three")
public class ThreeController {
@GetMapping("/get/three")
public String getContent() {
return "第三个服务,被第二个服务调用" + '\n';
}
}
@RestController
@RequestMapping("/four")
public class FourController {
@GetMapping("/get/four")
public String getContent() {
return "第四个服务,被第二个服务调用" + '\n';
}
}
@FeignClient(value = "cloud-micrometer-three-8603", path = "/three")
public interface MicrometerThreeFeignApi {
@GetMapping("/get/three")
String getContent();
}
@FeignClient(value = "cloud-micrometer-four-8604", path = "/four")
public interface MicrometerFourFeignApi {
@GetMapping("/get/four")
String getContent();
}
服务二
@FeignClient(value = "cloud-micrometer-two-8602", path = "/two")
public interface MicrometerTwoFeignApi {
@GetMapping("/get/twoOfOne")
String twoOfOne();
@GetMapping("/get/twoOfTwo")
String twoOfTwo();
}
@RestController
@RequestMapping("/two")
public class TwoController {
@Resource
private MicrometerThreeFeignApi micrometerThreeFeignApi;
@Resource
private MicrometerFourFeignApi micrometerFourFeignApi;
@GetMapping("/get/twoOfOne")
public String twoOfOne() {
return "第二个服务,被第一个服务调用,然后调用第三个服务" +
this.micrometerThreeFeignApi.getContent();
}
@GetMapping("/get/twoOfTwo")
public String twoOfTwo() {
return "第二个服务,被第一个服务调用,然后调用第四个服务" +
this.micrometerFourFeignApi.getContent();
}
}
服务一
@RestController
@RequestMapping("/one")
public class OneController {
@Resource
private MicrometerTwoFeignApi micrometerTwoFeignApi;
@GetMapping("/getOneOfOne")
public String getOneOfOne() {
return "第一个服务,调用第二个服务" + '\n' +
this.micrometerTwoFeignApi.twoOfOne();
}
@GetMapping("/getOneOfTwo")
public String getOneOfTwo() {
return "第一个服务,调用第二个服务" + '\n' +
this.micrometerTwoFeignApi.twoOfTwo();
}
}
测试
启动服务之后,分别调用127.0.0.1:one/getOneOfOne以及127.0.0.1:one/getOneOfTwo,多调用几次,然后稍等几分钟,再去ZipKin官网看看。
在官网,点击运行查询,就能出现如下结果,也可以点进去详细查看,就是一个请求对应的每个节点的耗时时间等内容。
然后去依赖页面,再次点击运行查询,就会出现一个调用图:
当然,在页面当中,还有许多详细信息,就不作过多介绍,不是我不想说,主要我也不知道。所以那些详细信息,就让咋们一起去探索吧,只希望别我毕业了,组件又换了。