Spring Cloud 07 - 分布式链路追踪APM
分布式链路追踪APM
文章目录
- 分布式链路追踪APM
- 一:Spring Cloud Sleuth
- 1:为什么要引入sleuth
- 2:sleuth基本术语
- 3:sleuth使用案例示例
- 3.1:构建zipkin-server工程
- 3.2:构建user-service模块
- 3.3:构建gateway-service
- 4:在链路数据中添加自定义数据
- 5:rabbitMQ组件通讯
- 5.1:改造zipkin-server
- 5.2:改造Zipkin Client
- 6:链路数据存储
- 6.1:存储在Mysql数据库
- 6.2:存储在ES (了解)
- 二:Skywalking
- 1:SkyWalking初探
- 1.1:APM系统
- 1.2:SkyWalking概述
- 1.2.1:基本概念
- 1.2.2:快速入门
- 1.2.3:架构设计
- 1.3:JavaAgent监控Spring Boot
- 1.3.1:agent下载解压
- 1.3.2:服务接入agent
- 2:链路追踪实战
- 2.1:持久化存储
- 2.1.1:es的安装和启动
- 2.1.2:进行skywalking的配置
- 2.1.3:使用mysql说明【补充】
- 2.2:微服务链路追踪
- 2.2.1:dubbo示例
- 2.2.2:spring cloud示例
- 2.3:日志采集
- 2.3.1:引入依赖
- 2.3.2:LogBack配置
- 2.4:告警通知
- 2.4.1:告警规则和webhook
- 2.4.2:演示一下
- 2.4.3:对接钉钉示例
- 2.5:细粒度追踪
- 2.5.1:引入依赖
- 2.5.2:获取traceId和注解
一:Spring Cloud Sleuth
Spring Cloud Sleuth 主要功能就是在分布式系统中提供追踪解决方案【sleuth -> 侦探;警犬】
兼容支持了 zipkin,你只需要在pom文件中引入相应的依赖即可
1:为什么要引入sleuth
微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务单元。
由于服务单元数量众多,业务的复杂性,如果出现了错误和异常,很难去定位。微服务架构中,必须实现分布式链路追踪。
举个例子,在微服务系统中,一个来自用户的请求,请求先达到前端A(如前端界面),然后通过远程调用,达到系统的中间件B、C(如负载均衡、网关等),最后达到后端服务D、E,后端经过一系列的业务逻辑计算最后将数据返回给用户。
对于这样一个请求,经历了这么多个服务,怎么样将它的请求过程的数据记录下来呢?这就需要用到服务链路追踪。
在Spring Cloud Sleuth中集成Zipkin非常的简单,只需要引入相应的依赖和做相关的配置即可。
2:sleuth基本术语
Spring Cloud Sleuth采用的是Google的开源项目Dapper的专业术语。
-
Span:基本工作单元,发送一个远程调度任务就会产生一个Span
- Span是一个64位ID唯一标识的,Span还有其他数据信息,比如摘要、时间戳事件、Span的ID、以及进度ID。
-
Trace:Trace是用另一个64位ID唯一标识的,一系列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时间戳便可以得到整个请求所消耗的时间。
3:sleuth使用案例示例
本文案例一共四个工程采用多Module形式。
- eureka-server工程,作为服务注册中心,eureka-server的创建过程这里不重复;
- zipkin-server作为链路追踪服务中心,负责存储链路数据;
- gateway-service作为服务网关工程,负责请求的转发, 同时它也作为链路追踪客户端,负责产生数据,并上传给zipkin-service;
- user-service为一个应用服务,对外暴露API接口,同时它也作为链路追踪客户端,负责产生数据。
3.1:构建zipkin-server工程
-
新建一个Module工程,取名为zipkin-server,其pom文件继承了主Maven工程的pom文件;
-
依赖导入:
- 作为Eureka Client,引入Eureka的起步依赖spring-cloud-starter-eureka
- 引入zipkin-server依赖和zipkin-autoconfigure-ui依赖,这两个依赖提供了Zipkin的功能和Zipkin界面展示的功能。
<!-- zipkin注解 -->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
- 在程序的启动类ZipkinServiceApplication加上@EnableZipkinServer开启ZipkinServer的功能
@SpringBootApplication
@EnableEurekaClient
@EnableZipkinServer // 加入这个注解
public class ZipkinServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinServerApplication.class, args);
}
}
- 在配置文件application.yml文件,指定程序名为zipkin-server,端口为9411,服务注册地址为http://localhost:8761/eureka/。
# 将自己加入到注册中心,这里指定注册中心的地址
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
# 声明自己的地址9411,因此zipkin-server的地址:http://localhost:9411
server:
port: 9411
spring:
application:
name: zipkin-server
3.2:构建user-service模块
-
在主Maven工程下建一个Module工程,取名为user-service,作为应用服务,对外暴露API接口。
-
pom文件继承了主Maven工程的pom文件,并引入:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- zipkin starter -->
<!-- 表明自己将会被zipkin追踪 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
-
在配置文件applicatiom.yml
-
指定了程序名为user-service,端口为8762
-
指定服务注册地址为http://localhost:8761/eureka/
-
指定Zipkin Server地址为http://localhost:9411
- spring.sleuth.sampler.percentage = 1.0,在默认的情况下,该值为0.1
-
# 声明eureka地址
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
# 声明自己的端口
server:
port: 8762
# 声明应用名称
spring:
application:
name: user-service
# 声明zipkin地址,就是zipkin-server的地址
zipkin:
base-url: http://localhost:9411
# base-url: http://localhost:9411/ # 服务端地址
# sender:
# #type: web # 数据传输方式,web 表示以 HTTP 报文的形式向服务端发送数据
# type: rabbit
# rabbitmq:
# queue: zipkin # 队列名称
# rabbitmq:
# host: 192.168.10.101 # 服务器 IP
# port: 5672 # 服务器端口
# username: guest # 用户名
# password: guest # 密码
# listener:
# direct:
# retry:
# enabled: true # 是否开启发布重试
# max-attempts: 5 # 最大重试次数
# initial-interval: 5000 # 重试间隔时间(单位毫秒)
# simple:
# retry:
# enabled: true # 是否开启消费者重试
# max-attempts: 5 # 最大重试次数
# initial-interval: 5000 # 重试间隔时间(单位毫秒)
sleuth:
sampler:
probability: 1.0 # 收集数据百分比,默认 0.1(10%)
- 建一个/user/hi的API接口,对外提供服务,代码如下
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/hi")
public String hi(){
return "I'm forezp";
}
}
启动类UserServiceApplication加上@EnableEurekaClient注解
3.3:构建gateway-service
-
新建一个名为gateway-service工程,这个工程作为服务网关
- 将请求转发到user-service,作为Zipkin客户端,需要将链路数据上传给Zipkin Server,同时它也作为Eureka Client。
-
它在pom文件除了需要继承主Maven工程的 pom,还需引入的依赖如下:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- zipkin starter -->
<!-- 表明自己将会被zipkin追踪 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
-
在application.yml文件,配置程序名为gateway-service,端口为5000:
-
服务注册地址为http://localhost:8761/eureka/
-
ZipkinServer地址为http://localhost:9411
-
# 将自己注册到注册中心
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
# 声明自己使用的端口是5000
server:
port: 5000
# 声明应用名称
spring:
application:
name: gateway-service
# zipkin声明
# zipkin:
# base-url: http://localhost:9411/ # 服务端地址
# sender:
# #type: web # 数据传输方式,web 表示以 HTTP 报文的形式向服务端发送数据
# type: rabbit
# rabbitmq:
# queue: zipkin # 队列名称
# rabbitmq:
# host: 192.168.10.101 # 服务器 IP
# port: 5672 # 服务器端口
# username: guest # 用户名
# password: guest # 密码
# listener:
# direct:
# retry:
# enabled: true # 是否开启发布重试
# max-attempts: 5 # 最大重试次数
# initial-interval: 5000 # 重试间隔时间(单位毫秒)
# simple:
# retry:
# enabled: true # 是否开启消费者重试
# max-attempts: 5 # 最大重试次数
# initial-interval: 5000 # 重试间隔时间(单位毫秒)
sleuth:
sampler:
probability: 1.0 # 收集数据百分比,默认 0.1(10%)
zipkin:
base-url: http://localhost:9411 # 声明 zipkin server地址
# 设置网关映射:以/user-api/**开头的Uri请求,转发到服务名为user-service的服务
zuul:
routes:
api-a:
path: /user-api/**
serviceId: user-service
在程序的启动类GatewayServiceApplication
- 加上@EnableEurekaClient注解开启Eureka Client
- 加上@EnableZuulProxy注解,开启Zuul代理功能。代码如下:
@SpringBootApplication
@EnableZuulProxy // 开启zuul代理功能
@EnableEurekaClient
public class GatewayServiceApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayServiceApplication.class, args);
}
}
4:在链路数据中添加自定义数据
现在需要实现这样一个功能:需要在链路数据中加上操作人。
这需要在gateway-service上实现。建一个ZuulFilter过滤器,它的类型为“post”,order为900,开启拦截。
在拦截逻辑方法里,通过Tracer的addTag方法加上自定义的数据,比如本案例中加入了链路的操作人。
另外也可以在这个过滤器中获取当前链路的traceId信息,traceId作为链路数据的唯一标识,可以存储在log日志中,方便后续查找。
@Component
public class LoggerFilter extends ZuulFilter {
@Autowired
Tracer tracer;
@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
@Override
public int filterOrder() {
return 900;
}
@Override
public boolean shouldFilter() {
return true;
}
// 在链路中加入操作人是forezp
@Override
public Object run() {
// tracer.addTag
tracer.addTag("operator","cuihaida");
System.out.print(tracer.getCurrentSpan().traceIdString());
return null;
}
}
5:rabbitMQ组件通讯
在上述的案例中,最终gateway-service收集的数据,是通过Http上传给zipkin-server的
在Spring Cloud Sleuth中支持消息组件来通讯的,下面使用RabbitMQ来通讯。
5.1:改造zipkin-server
-
在pom文件将zipkin-server的依赖去掉
-
加上spring-cloud-sleuth-zipkin-stream和spring-cloud-starter-stream-rabbit,代码如下:
<dependency>
<!-- zipkin流式管理 -->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
<dependency>
<!-- rabbitMQ流式 -->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
- 在application.yml配置上RabbitMQ的配置:
# 声明消息中间件的信息,host & port & username & password
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
- 在程序的启动类ZipkinServerApplication上@EnableZipkinStreamServer注解,开启ZipkinStreamServer。代码如下:
@SpringBootApplication
@EnableEurekaClient
@EnableZipkinStreamServer // 引入stream注解
public class ZipkinServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinServerApplication.class, args);
}
}
5.2:改造Zipkin Client
包括gateway-service、user-service
- 在pom文件中将spring-cloud-starter-zipkin去掉
- 新增spring-cloud-sleuth-zipkin-stream和spring-cloud-starter-stream-rabbit
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
- 同时在applicayion.yml文件加上RabbitMQ的配置,同zipkin-server工程。
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
- 就将链路的上传数据从Http改了为用消息代组件RabbitMQ。
6:链路数据存储
6.1:存储在Mysql数据库
Zipkin支持Mysql、Elasticsearch、Cassandra存储
- 首先,在zipkin-server工程加上Mysql的连接依赖mysql-connector-java,JDBC的起步依赖spring-boot-starter-jdbc
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
- 在配置文件application.yml加上数据源的配置,包括数据库的Url、用户名、密码、连接驱动,另外需要配置zipkin.storage.type为mysql
spring:
datasource:
url: jdbc:mysql://localhost:3306/zipkin?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
# 设置存储方式是mysql
zipkin:
storage:
type: mysql
- 另外需要在Mysql数据库中初始化数据库脚本
CREATE TABLE IF NOT EXISTS zipkin_spans (
trace_id_high BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
trace_id BIGINT NOT NULL,
id BIGINT NOT NULL,
name VARCHAR(255) NOT NULL,
parent_id BIGINT,
debug BIT(1),
start_ts BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
duration BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query'
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
ALTER TABLE zipkin_spans ADD UNIQUE KEY(trace_id_high, trace_id, id) COMMENT 'ignore insert on duplicate';
ALTER TABLE zipkin_spans ADD INDEX(trace_id_high, trace_id, id) COMMENT 'for joining with zipkin_annotations';
ALTER TABLE zipkin_spans ADD INDEX(trace_id_high, trace_id) COMMENT 'for getTracesByIds';
ALTER TABLE zipkin_spans ADD INDEX(name) COMMENT 'for getTraces and getSpanNames';
ALTER TABLE zipkin_spans ADD INDEX(start_ts) COMMENT 'for getTraces ordering and range';
CREATE TABLE IF NOT EXISTS zipkin_annotations (
trace_id_high BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
trace_id BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',
span_id BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',
a_key VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',
a_value BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
a_type INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',
a_timestamp BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',
endpoint_ipv4 INT COMMENT 'Null when Binary/Annotation.endpoint is null',
endpoint_ipv6 BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
endpoint_port SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',
endpoint_service_name VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
ALTER TABLE zipkin_annotations ADD UNIQUE KEY(trace_id_high, trace_id, span_id, a_key, a_timestamp) COMMENT 'Ignore insert on duplicate';
ALTER TABLE zipkin_annotations ADD INDEX(trace_id_high, trace_id, span_id) COMMENT 'for joining with zipkin_spans';
ALTER TABLE zipkin_annotations ADD INDEX(trace_id_high, trace_id) COMMENT 'for getTraces/ByIds';
ALTER TABLE zipkin_annotations ADD INDEX(endpoint_service_name) COMMENT 'for getTraces and getServiceNames';
ALTER TABLE zipkin_annotations ADD INDEX(a_type) COMMENT 'for getTraces';
ALTER TABLE zipkin_annotations ADD INDEX(a_key) COMMENT 'for getTraces';
ALTER TABLE zipkin_annotations ADD INDEX(trace_id, span_id, a_key) COMMENT 'for dependencies job';
CREATE TABLE IF NOT EXISTS zipkin_dependencies (
day DATE NOT NULL,
parent VARCHAR(255) NOT NULL,
child VARCHAR(255) NOT NULL,
call_count BIGINT,
error_count BIGINT
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(day, parent, child);复制代码
6.2:存储在ES (了解)
- 首先在pom文件,加上zipkin的依赖和zipkin-autoconfigure-storage-elasticsearch-http的依赖,代码如下:
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin</artifactId>
<version>1.28.0</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
<version>1.28.0</version>
</dependency>
-
在application.yml文件加上Zipkin的配置,配置了zipkin的存储类型为elasticsearch,使用的StorageComponent为elasticsearch。
-
然后需要配置elasticsearch,包括hosts,可以配置多个,用“,”隔开;index为zipkin等,具体配置如下:
zipkin:
storage:
type: elasticsearch
StorageComponent: elasticsearch
elasticsearch:
cluster: elasticsearch # es集群名称
max-requests: 30 # 最大请求并发数
index: zipkin # 索引名称
index-shards: 3 # 分片数
index-replicas: 1 # 副本数
hosts: localhost:9200 # es地址
二:Skywalking
1:SkyWalking初探
1.1:APM系统
APM(Application Performance Management) 即应用性能管理系统,是对企业系统即时监控以实现对应用程序性能管理和故障管理的系统化解决方案
应用性能管理,主要是针对企业的关键业务应用进行检测,优化,提高企业应用的可靠性和质量,保证用户得到良好的服务,降低总拥有成本。
APM系统可以帮助理解系统的行为,用于分析性能问题的工具,以便于发生故障的时候,可以快速地定位和解决问题。
APM比较关注的是三个方面:日志(log) + 指标(metrice) + 链路追踪(traces)
主流的APM落地实现落地方案系统:日志 -> ELK Stack, 指标 -> Prometheus,链路追踪 -> SkyWalking
遵循OpenTracing标准
1.2:SkyWalking概述
1.2.1:基本概念
官网:https://skywalking.apache.org
SkyWalking是一个开源的可观测平台,用于从服务和云原生基础设施收集,分析,聚合和可视化数据
SkyWalking提供了一种简便的方式来清晰的观测分布式系统,甚至横跨多个云平台
SkyWalking是一个现代化的应用程序性能监控(APM)系统,尤其专为云原生,基于容器的分布式系统设计。
SkyWalking的核心功能
- 服务,服务实例,端点(URL)指标分析
- 根本原因分析,在运行时上分析由进程内代理和ebpf分析器支持的代码
- 业务拓扑图分析
- 服务实例和端点(URL)的依赖关系分析
- 服务和端点检测速度分析,性能优化
- …
SkyWalking的特点
- java生态,功能丰富,社区活跃,迭代迅速
- 链路追踪,拓扑分析能力强,采用先进的流式拓扑分析
- 插件丰富,探针无侵入
- 微内核 + 插件架构,存储,集群管理
- …
关键概念
1.2.2:快速入门
1:下载对应的服务端和Agent端【上报服务数据给服务端,进行链路追踪】
然后进入bin目录,选择启动脚本运行:
- startup.sh -> 会先后启动OAP服务和UI服务
- oapService.sh -> 单独启动OAP服务,第一次启动需要初始化数据,可能比较慢
- webappService.sh -> 单独启动UI服务
任务服务检查是否启动成功,看<skyWalking Home>/logs
下面的日志文件就可以了
webapp的端口是多少呢?
1.2.3:架构设计
上一个版本架构图
现在版本架构图
skyWalking四大组件
Agent = 探针 => 收集对应服务的数据然后上传到SkyWalking上
设计目标
- 可观测性 -> Agent
- 拓扑结构 -> 链路调用关系
- 轻量级 -> 作为监控系统,不能喧宾夺主,一定要轻量级
- 可拔插 -> 可以选择是否使用,而不是集中式服务一定要使用,可插拔和轻量级相辅相成
- 可移植 -> 多平台可用
1.3:JavaAgent监控Spring Boot
1.3.1:agent下载解压
1.3.2:服务接入agent
有三种基本方式配置agent -> 配置文件 || 启动脚本 || IDE开发工具
#! /bin/bash
# ============== 下面是启动脚本的方式的基本的实例 ============
export SW_AGENT_NAME=skywalking-demo # 探针的名称,一般指定为监控应用的名称
export SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 # Collector地址,指向的是服务器中OAP服务
export SW_AGENT_SPAN_LIMIT=1000 # 配置链路的最大Span数量,默认是300个
export JAVA_AGENT=-javaagent:<探针Jar的位置>
java $JAVA_AGENT -jar skywalking-demo-1.0.0-SNAPSHOT.jar #启动程序
# ============= 下面是通过idea启动配置中添加如下的jvm参数 ==================
-javaagent:<探针Jar的位置>
-DSW_AGENT_NAME=skywalking-demo # 探针的名称,一般指定为监控应用的名称
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 # Collector地址,指向的是服务器中OAP服务
2:链路追踪实战
2.1:持久化存储
skywalking默认使用的是H2数据库,H2是一个内存型数据库,如果skywalking重启了,数据就没有了【因为没有持久化到磁盘中】
因此需要对skywalking进行持久化存储的配置:一般采用的是es & mysql
因为链路追踪的日志等信息回比较多,所以一般存储在es中,下面将演示使用es进行持久化存储
2.1.1:es的安装和启动
前置工作 -> 解压es,移动目录&重命名,新建一个es用户和组【因为es不支持root启动】,赋予新用户es文件夹的权限
tar -zxf elasticsearch-7.17.0-linux-x86.64.tar.gz # 解压
mv elasticsearch-7.17.0-linux-x86.64 /usr/local/elasticsearch-7.17.0 # 移动到/usr/local下并重命名
groupadd elasticsearch # 创建一个组叫做elasticsearch
useradd elasticsearch -g elasticsearch # 创建一个用户名:elasticsearch,所属组 elasticsearch
su elasticsearch # 切换到这个新建的用户下
chown -R elasticsearch:elasticsearch elasticsearch-7.17.0/ # 权限赋予
配置es的配置文件:在
config/jvm.options
中
vim config/jvm.options
启动并查看启动日志
bin/elasticsearch -d # -d是指定后台启动
tail -f log/elasticsearch.log # 查看日志 -f 追加型
测试下是否启动成功 -> curl http://localhost:9200 # you know, for search
自此,es配置启动完成
2.1.2:进行skywalking的配置
修改配置文件
exit # 退出elasticsearch这个用户,回到root
cd skywalking-apm-9.3/
vim config/application.yml
只需要改下面几个部分
重启oap服务
2.1.3:使用mysql说明【补充】
- skywalking中配置信息修改:
- 修改
storage.selector
为mysql - 在下面指定url, username, password
- 默认是不支持mysql的,需要将mysql的驱动包拷贝到oap-lib目录下才可以
2.2:微服务链路追踪
2.2.1:dubbo示例
provider
主启动类支持dubbo并且实现对应的接口方法
consumer
2.2.2:spring cloud示例
gateway -> order(推送消息给finance) -> user(获取支付用户和收款用户) & product(查找商品)
// order核心代码
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
private final UserFeignService userFeignService;
private final ProductFeignService productFeignService;
private final RocketMQTemplate rocketMQTemplate;
// 构造器注入
public OrderServiceImpl(UserFeignService userFeignService, ProductFeignService productFeignService, RocketMQTemplate rocketMQTemplate) {
this.userFeignService = userFeignService;
this.productFeignService = productFeignService;
this.rocketMQTemplate = rocketMQTemplate;
}
@Override
public OrderDTO detailById(Long id) {
Order order = super.getById(id);
if (order != null) {
OrderDTO dto = new OrderDTO();
BeanUtils.copyProperties(order, dto);
// 查找支付用户
dto.setPalyerUser(userFeignService.findById(order.getPayerUserId()));
// 查找收款用户
dto.setReceiverUser(userFeignService.findById(order.getReceiveUserId()));
// 查找商品
dto.setProductId(productFeignService.findById(order.getProductId()));
// 发送MQ消息给finance
// ORDER -> key
// DETAILS -> tag
SendResult result = rocketMQTemplate.syncSend("ORDER:DETAILS", dto);
log.info("发送状态:{}", result.getSendStatus());
return dto;
}
return null;
}
}
接受MQ消息
product核心逻辑
所有的服务都使用这个
2.3:日志采集
2.3.1:引入依赖
<!-- skyWalking-log -->
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>8.14.0</version>
</dependency>
2.3.2:LogBack配置
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 根节点configuration -->
<!-- scan属性 -> 是否开启热加载配置文件,此属性设置成为true之后,配置文件如果发生改变,将会被重新加载,默认就是true -->
<!-- scanPeriod属性 -> 配置热加载的间隔时间,默认是1分钟,如果要关闭热加载,设置为5s检查一次 -->
<!-- debug属性 -> 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false -->
<configuration scan="true" scanPeriod="5 seconds">
<!-- appender负责写日志的组件,它有两个必要属性name和class。name指定appender名称,class指定appender的全限定名。 -->
<!-- 日志输出到控制台 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!-- 日志输出格式 -->
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 异步输出 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<!-- 丢弃阈值 -->
<discardingThreshold>0</discardingThreshold>
<!-- 队列大小 -->
<queueSize>1024</queueSize>
<!-- 是否启用队列满丢弃日志记录功能 -->
<neverBlock>true</neverBlock>
<!-- 引用 -->
<appender-ref ref="STDOUT"/>
</appender>
<!-- grpc日志输出,为skywalking控制台输出做准备 -->
<appender name="grpc-log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</encoder>
</appender>
<!-- 根节点, 默认的日志级别是DEBUG,这里指定成为info -->
<root level="INFO">
<appender-ref ref="ASYNC"/>
<appender-ref ref="grpc-log"/>
</root>
</configuration>
请求调用之后,就可以在控制台看到对应的TID信息,说明链路追踪日志打印完成
2.4:告警通知
2.4.1:告警规则和webhook
skywalking有如下告警规则:
- metrice-name:指标的名称,也是OAL脚本中的指标名称,可以配置告警的指标有:服务,实例,端点,服务关系,实例关系,端点关系
- op:操作符,例如 >, <等等
- threshold:阈值
- period:告警规则多久被检查一次,是一个时间窗口
- count:在一个时间窗口中,满足op超过阈值的次数达到count值,就会触发告警
- slience-period:在时间N中触发告警后,在N + slience-period这段时间不告警
- message:告警时通知的消息
触发了告警之后通知到哪里呢? -----> 将会通知到webhooks的配置中
2.4.2:演示一下
写一个读取通知的小demo
将这个服务推送到服务器并启动
webhook添加通知路径
重启skywalking
服务触发告警之后将会看到(不能立刻看到,2分钟后大概)
2.4.3:对接钉钉示例
进入skywalking官网,选择对应的版本
文档选择
钉钉中创建钉钉机器人
也可以通过群聊添加
修改配置文件
生成钉钉机器人的URL地址
改好之后,重启OAP
2.5:细粒度追踪
之前的链路追踪展示的效果粒度较粗,因此引入skyWalking工具包,细粒度化
2.5.1:引入依赖
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>8.14.0</version>
</dependency>
2.5.2:获取traceId和注解
@RequestMapping("/trace")
public void traceId() {
// 可以向上下文对象中绑定key/value数据
TraceContext.putCorrelation("name", "xiaoliu");
// 获取traceId
log.info("get trace id: ()", TraceContext.traceId());
}
@Trace标签和@Tag标签
有了这两个标签,到时候看UI界面就会有对应的信息