7.Skywalking与zipkin链路追踪
SkyWalking是一款开源的分布式服务跟踪系统,用于解决微服务架构下的调用链路追踪问题。文章介绍了其工作原理、系统架构(包含Agent、OAP、Storage和UI四个部分)以及核心概念(trace、segment和span)。同时详细讲解了Linux环境搭建步骤,包括主机配置、虚拟内存调整、文件描述符设置等,并提供了Elasticsearch的安装与启动指南。适用于生产环境下的Spring
整理资料下载:
链接: https://pan.baidu.com/s/16_LrfRPkbAjTMqaFNHz_lg?pwd=e3jy
提取码: e3jy
–来自百度网盘超级会员v6的分享
一、Skywalking
随着分布式系统规模的越来越大,各微服务间的调用关系也变得越来越复杂
一般情况下, 一个由客户端发起的请求在后端系统中会经过许多不同的微服务调用才能完成最终处理,而这些调用过程形成了一个复杂的分布式服务调用链路
那么也就带来了一系列问题:怎样快速发现并定位问题? 怎样判断故障影响范围? 各部分调用链路性能是怎样的? 对于这些问题, 通过分布式服务跟踪系统可以解决
生产环境下, Spring Cloud Alibaba 经常会使用 SkyWalking 作为调用链跟踪系统
国内的分布式跟踪系统
- Hydra(京东)
- CAT(大众点评)
- Watchman(新浪)
- Micoscope(唯品会)
- Eagleeye(淘宝,未开源)
国际上使用较多的是
- Zipkin(Twitter)
- Skywalking
1、SkyWalking 简介
1.1.1、简介
SkyWalking 是由国内开源爱好者吴晟开源并提交到 Apache 孵化器的产品,现在已是Apache 的顶级项目。
其是一个开源的 APM(Application Performance Management,应用性能管理系统) 和 OAP 平台(Observability Analysis Platform,可观测性分析平台)
官网地址https://skywalking.apache.org/
1.1.2、工作原理
- 在被监测应用中插入探针,以无侵入方式自动收集所需指标,并自动推送到OAP 系统平台
- OAP 会将收集到的数据存储到指定的存储介质 Storage
- UI 系统通过调用 OAP提供的接口,可以实现对相应数据的查询
1.1.3、系统架构
SkyWalking 系统整体由四部分构成
-
Agent
探针,无侵入收集,是被插入到被监测应用中的一个进程
其会将收集到的监控指标自动推送给 OAP 系统
-
OAP
Observability Analysis Platform(可观测性分析平台)
其包含一个收集器 Collector,能够将来自于 Agent 的数据收集并存储到相应的存储介质 Storage
-
Storage
数据中心,用于存储由 OAP 系统收集到的链路数据
支持 H2、 MySQL、ElasticSearch 等
默认使用的是 H2(测试使用),推荐使用 ElasticSearch(生产使用)
-
UI
一个独立运行的可视化 Web 平台,其通过调用 OAP 系统提供的接口,可以对存储在 Storage 中的数据进行多维度查询
1.1.4、trace /segment/span
-
trace (轨迹)
跟踪单元是从客户端所发起的请求抵达被跟踪系统的边界开始,到被跟踪系统向客户返回响应为止的过程,这个过程称为一个 trace
-
span(跨度)
每个 trace 中会调用若干个服务,为了记录调用了哪些服务,以及每次调用所消耗的时间等信息,在每次调用服务时,埋入一个调用记录,这样两个调用记录之间的区域称为一个 span
一个 trace 由若干个有序的 span 组成
-
segment(片断)
一个 trace 的一个片断,可以由多个 span 组成
为了唯一的标识 trace、 span 与 segment,跟踪系统一般会为每个 trace、 span 与 segment都指定了一个唯一标识,即 traceId、 spanId 与 segmentId
2、环境搭建
1.2.1、单机搭建
1.2.1.1、Linux环境准备
(1)、克隆并配置主机
克隆一台干净的主机,并修改配置
-
修改主机名
vi /etc/hostname -
修改网络配置
vi /etc/NetworkManager/system-connections/ens33.nmconnection method=manual address1=192.168.0.112/24

(2)、修改虚拟内存空间大小
系统变量 vm.max_map_count 是系统中分配的虚拟内存空间大小
-
永久修改
在/etc/sysctl.conf 文件最后插入如下一行内容
vim /etc/sysctl.conf # 添加如下内容 vm.max_map_count=262144这种修改方式是永久修改,将其值调整为了 256K

永久修改的生效需要重启系统
此时查看系统变量
sysctl -a |grep vm.max_map_count
-
临时修改
可以通过以下命令临时修改其值
sysctl -w vm.max_map_count=262144
一般情况下我们会永久修改与临时修改均进行设置,这样的话,本次不用重启,以后重启后其值也会永久改变,一举两得
查看修改后的值发现其值已经修改过了
sysctl -a |grep vm.max_map_count
(3)、修改最大文件描述符数量
在/etc/security/limits.conf 文件最后插入如下内容
vi /etc/security/limits.conf
# 插入如下内容
* soft nofile 65536
* hard nofile 65536
- 星号(*)表示对所有用户
-
soft:柔和边界设定(可以超过该设定值,但超过后会有警告)
-
hard:严格边界设定(不允许超过该设定的值 )
-
nofile:每个进程可打开的文件数的最大数量

(4)、修改用户最大线程数
在/etc/security/limits.conf 文件最后插入如下内容
vi /etc/security/limits.conf
# 插入如下内容
* soft nproc 4096
* hard nproc 4096
- 星号(*)表示对所有用户
-
soft:柔和边界设定(可以超过该设定值,但超过后会有警告)
-
hard:严格边界设定(不允许超过该设定的值 )
-
nproc:每个用户可创建的进程数的最大数量

(5)、创建用户与密码
ES 不能使用 root 用户进行启动,所以需要创建新的用户,并为该用户指定登录密码
下面的两行命令创建了用户 zhangsan,指定的登录密码为123qwe456asd
# 创建用户
useradd zhangsan
# 设置密码
echo "123qwe456asd" |passwd zhangsan --stdin

1.2.1.2、ES安装与启动
(1)、下载安装包
从 ES 官网 https://elastic.co/下载针对 Linux 系统的安装包
这里下载的版本是 elasticsearch-9.0.0-rc1.tar.gz
(2)、上传与解压
-
将安装包上传到/usr/local/install目录下
-
解压缩
tar -zxvf /usr/local/install/elasticsearch-8.17.4-linux-x86_64.tar.gz -C /usr/local/install/
-
重命名
mv /usr/local/install/elasticsearch-8.17.4 /usr/local/install/elasticsearch
(3)、修改所有者
进入 es 目录,可以看到其包含的内容,其所有者全部为 root:root
ll /usr/local/install/elasticsearch

但 ES 不能使用 root用户进行启动,所以现在要将其修改为新建用户 zhangsan:zhangsan
chown -R zhangsan:zhangsan /usr/local/install/elasticsearch

再次查看权限
ll /usr/local/install/elasticsearch

(4)、修改 elasticsearch.yml
修改 config/ elasticsearch.yml 文件中的以下几个位置
vi /usr/local/install/elasticsearch/config/elasticsearch.yml

(5)、启动es
-
切换用户
将用户切换为 zhangsan
su zhangsan -
启动命令
运行 ES 解压目录的 bin 目录下的 elasticsearch 命令来启动 ES
/usr/local/install/elasticsearch/bin/elasticsearch
(6)、再次修改配置文件
上面启动es后会有安全配置的参数,需要重新配置
修改 config/ elasticsearch.yml 文件中的以下几个位置
vi /usr/local/install/elasticsearch/config/elasticsearch.yml

(7)、启动es
-
切换用户
将用户切换为 zhangsan
su zhangsan -
启动命令
运行 ES 解压目录的 bin 目录下的 elasticsearch 命令来启动 ES
/usr/local/install/elasticsearch/bin/elasticsearch -d -
查看日志
tail -100f /usr/local/install/elasticsearch/logs/my-application.log -
成功标识
在浏览器中访问 ES 服务器时可以看到以下页面,说明启动成功了
http://192.168.0.112:9200/

(8)、ES 的关闭
直接在服务器窗口中 Ctrl + C 即可关闭 ES

1.2.1.3、SW安装与启动
SW 的安装与启动,即 OAP Server 的安装与启动,当前其同时也会将 UI Server 也启动
(1)、下载 SW
在官网https://skywalking.apache.org/downloads/下载页面下载最新版本的 SkyWalking APM

(2)、安装JDK
安装JDK21并配置环境变量
安装前先在官网查看JDK版本依赖关系,进入文档选项

找到JDK版本依赖关系,然后安装对应版本的JDK即可

(3)、解压安装
解压缩安装包即可

(4)、配置服务注册
默认注册方式
服务注册这里默认走的单机模式,下面还有zookeeper、nacos等注册中心,根据需要配置即可

修改注册
假设需要使用nacos作为配置中心,可以按下述描述进行修改配置
cluster:
selector: ${SW_CLUSTER:nacos}
nacos:
# 注册的服务名称
serviceName: ${SW_SERVICE_NAME:"SkyWalking_OAP_Cluster"}
# Nacos 地址
hostPort: ${SW_CLUSTER_NACOS_HOST_PORT:192.168.1.110:8848}
# Nacos 命名空间
namespace: ${SW_CLUSTER_NACOS_NAMESPACE:"public"}
# Nacos 上下文路径(可选)
contextPath: ${SW_CLUSTER_NACOS_CONTEXT_PATH:""}
# Nacos 用户名(如果启用了认证)
username: ${SW_CLUSTER_NACOS_USERNAME:"nacos"}
# Nacos 密码(如果启用了认证)
password: ${SW_CLUSTER_NACOS_PASSWORD:"nacos"}
# Nacos 访问密钥(可选)
accessKey: ${SW_CLUSTER_NACOS_ACCESSKEY:""}
# Nacos 秘钥(可选)
secretKey: ${SW_CLUSTER_NACOS_SECRETKEY:""}
# 内部通信主机(可选)
internalComHost: ${SW_CLUSTER_INTERNAL_COM_HOST:""}
# 内部通信端口(可选)
internalComPort: ${SW_CLUSTER_INTERNAL_COM_PORT:-1}

(5)、配置存储数据库
配置文件位置 安装目录/config/application.yml
默认存储

selector决定了使用哪种存储后端,默认值是banyandb
使用ES存储
使用ES的配置

storage:
selector: ${SW_STORAGE:elasticsearch} # 设置为 elasticsearch
elasticsearch:
namespace: ${SW_NAMESPACE:""} # 命名空间(可选)
clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:192.168.1.110:9200} # Elasticsearch 地址
protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:"http"} # 协议,默认为 http
connectTimeout: ${SW_STORAGE_ES_CONNECT_TIMEOUT:3000} # 连接超时时间(毫秒)
socketTimeout: ${SW_STORAGE_ES_SOCKET_TIMEOUT:30000} # 套接字超时时间(毫秒)
responseTimeout: ${SW_STORAGE_ES_RESPONSE_TIMEOUT:15000} # 响应超时时间(毫秒)
numHttpClientThread: ${SW_STORAGE_ES_NUM_HTTP_CLIENT_THREAD:0} # HTTP 客户端线程数(默认为 0)
user: ${SW_ES_USER:""} # 用户名(如果启用了认证)
password: ${SW_ES_PASSWORD:""} # 密码(如果启用了认证)
dayStep: ${SW_STORAGE_DAY_STEP:1} # 每个索引的时间跨度(天)
indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:1} # 每个索引的分片数
indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:1} # 每个索引的副本数
使用MySQL的配置
-
添加驱动
连接mysql需要在
安装目录\oap-libs\下放入mysql-connector-java-8.0.22.jar包,默认无
-
修改配置

-
创建数据库
如果使用的是mysql,则需要创建一个数据库,默认的数据库是swtest,不需要建表
(6)、修改 UI 端口
默认情况下SW 的 UI 应用端口号为 8080,其会与后面应用的端口号冲突
该端口号的修改位置是在 SW 解压目录下的 webapp 目录中的 application.yml 文件中

(7)、启动SW
查看启动指令
bin 目录中的命令分为三类
-
OAP 启动相关命令
-
UI 启动相关命令
直接运行 startup 命令,可以同时启动 OAP 与 UI
-
整体启动相关命令

从这里也可以看出SW 服务端中包含 OAP Server 与 UI Server 这两部分
运行启动指令
这里直接运行安装目录下的/bin/startup.bat 文件
启动后即可看到两个窗口
- 一个是Collector(OAP Server)
- 一个是 Webapp(UIServer)

(8)、查看日志
日志文件在安装目录下的logs目录中

(9)、测试
能够在浏览器中看到如下页面,说明 SW 启动成功
访问: http://localhost:9999/

1.2.2、集群搭建
生产环境下很多公司为了方便, SW 一般不搭建集群
因为 SW 的短暂的宕机对业务是没有影响的
SW 集群纯属于性能检测模块,非业务模块
(1)、修改集群配置
修改安装目录/config/application.yml,默认是单机版的,集群搭建需要注册中心,这里修改成nacos注册中心

(2)、修改存储配置
修改安装目录/config/application.yml中的数据存储配置
集群搭建必须使用公共的存储方式,生产建议使用ES存储(参考单机配置)
如果使用的是mysql,则需要创建一个数据库,默认的数据库是swtest,不需要建表

(3)、修改UI端口
默认情况下,SW 的 UI 应用端口号为 8080,其会与后面应用的端口号冲突
该端口号的修改位置是在 SW 解压目录下的 webapp 目录中的 application.yml 文件中

(4)、启动测试
分别启动每台服务器即可,但是windows启动不能使用伪集群方式,需要多台电脑分别配置启动。因为skywalking9版本以后需要节点的IP不能一样,不能通过单机+端口号方式区分了
这里最好使用虚拟机方式搭建搭建过程一样
3、Agent自动监控微服务调用
1.3.1、搭建项目
1.3.1.1、搭建Account项目
(1)、引入依赖
<?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.seata</groupId>
<artifactId>SkywalkingDemo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.account</groupId>
<artifactId>account-service</artifactId>
<dependencies>
<!--feign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--负载均衡依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos 服务注册-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
</dependencies>
</project>
(2)、配置文件
##指定本服务的端口号
server:
port: 8081
spring:
#设置当前应用的名称
application:
name: account-service
#数据库连接配置
datasource:
url: jdbc:mysql://192.168.1.110:3306/nacos?characterEncoding=UTF-8&useSSL=false&useUnicode=true&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
cloud:
# nacos相关的配置
nacos:
discovery:
# 配置注册中心地址
server-addr: 192.168.1.110:8848
# 设置连接nacos的用户名、密码
username: nacos
password: nacos
(3)、测试Controller
package com.account.controller;
import com.account.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/account")
public class AccountController {
@Autowired
private AccountService accountService;
/**
* 修改用户账户信息
*
* @return
*/
@RequestMapping("/getAccount")
public String getAccount() {
System.out.println("AccountController getAccount 执行");
accountService.getAccount();
return "success";
}
}
(4)、测试Service
package com.account.service;
import com.account.client.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccountService {
@Autowired
ProductService productService;
public void getAccount() {
System.out.println("AccountService getAccount 执行");
productService.getProduct();
}
}
(5)、Feign配置
package com.account.config;
import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* 自定义Feign配置类
*/
@Configuration
public class FeignConfig {
@Bean
public Retryer feignRetryer() {
return new Retryer.Default(100, SECONDS.toMillis(1), 5);
}
}
(6)、FeignClient配置
package com.account.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 用于访问Product服务的feign
*/
@FeignClient("product-service")
public interface ProductService {
@RequestMapping(value = "/product/getProduct")
String getProduct();
}
(7)、启动类
开启feign
package com.account;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class AccountApplication {
public static void main(String[] args) {
SpringApplication.run(AccountApplication.class, args);
}
}
1.3.1.2、搭建product项目
(1)、引入依赖
<?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.seata</groupId>
<artifactId>SkywalkingDemo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.product</groupId>
<artifactId>product-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos 服务注册-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
</dependencies>
</project>
(2)、配置文件
##指定本服务的端口号
server:
port: 8082
spring:
#设置当前应用的名称
application:
name: product-service
#数据库连接配置
datasource:
url: jdbc:mysql://192.168.1.110:3306/nacos?characterEncoding=UTF-8&useSSL=false&useUnicode=true&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
cloud:
# nacos相关的配置
nacos:
discovery:
# 配置注册中心地址
server-addr: 192.168.1.110:8848
# 设置连接nacos的用户名、密码
username: nacos
password: nacos
(3)、测试Conntroller
package com.product.controller;
import com.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/getProduct")
public void getProduct() {
productService.getProduct();
}
}
(4)、测试Sservice
package com.product.service;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
public void getProduct() {
System.out.println("ProductService getProduct 执行");
}
}
1.3.2、下载Agent客户端
进入官网https://skywalking.apache.org/下载

1.3.3、引入Agent
安装包下载完毕后,解压缩,将解压包放到服务器的任意位置即可,只要启动微服务之前启动Agent即可
由于我这里使用的是本地电脑,为了方便区分,这里将Agent放到每个项目的路径下

1.3.4、配置Agent
(1)、查找官方文档
参考官方文档

找到配置文档

(2)、安装JDK
已经安装
(3)、修改应用名
修改每个skywalking-agent/config/agent.config配置文件
-
修改应用名
agent.service_name=${SW_AGENT_NAME:account}

(4)、修改SW地址
修改每个skywalking-agent/config/agent.config配置文件
-
修改OAP服务器的地址
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:127.0.0.1:11800}

(5)、启动Agent
需要启动Agnet服务(每台机器都需要启动)

Agent客户端需要在每个微服务启动前启动,实际中可以在微服务启动脚本中添加启动命令,这样在启动微服务时就会自动启动Agent,Agent启动后就会自动监控微服务
这里为了方便,在启动程序中添加启动参数

添加虚拟机参数

选择Agent客户端的位置
-javaagent:H:\WorkSpace\WorkSpace3\SkywalkingDemo\account-service\skywalking-agent\skywalking-agent.jar

同理添加project-service服务的参数
(6)、启动服务测试
启动每个微服务
多次访问接口http://localhost:8081/account/getAccount
(7)、查看sw监控
访问http://localhost:9999/,查看服务监控信息
-
服务信息

-
拓扑结构

-
Trace信息

1.3.5、使用总结
Agent自动追踪方式主需要引入Agent客户端,配置好监控的微服务名和连接的SW地址即可,在微服务启动时先启动Agent客户端即可完成链路追踪
4、AgentAPI监控服务内调用
Skywalking 除了为客户端提供了探针 Agent 外,还提供了相应的 API,用于在代码中对调用跟踪进行处理
当然该方式会对代码形成侵入,会污染代码
1.4.1、TraceContext
这里以 TraceContext 为例来简单学习 Tracing API 的使用
TraceContext是跟踪上下文,通过其可以获取到 traceId,也可在其中完成属性的写与读
源码如下
package org.apache.skywalking.apm.toolkit.trace;
import java.util.Optional;
public class TraceContext {
public TraceContext() {
}
// 获取traceId
public static String traceId() {
return "";
}
// 获取segmentId
public static String segmentId() {
return "";
}
// 获取spanId
public static int spanId() {
return -1;
}
// 获取添加的自定义信息
public static Optional<String> getCorrelation(String key) {
return Optional.empty();
}
// 往上下文中添加自定义的键值对数据
public static Optional<String> putCorrelation(String key, String value) {
return Optional.empty();
}
}
(1)、导入依赖
在需要使用 Tracing API 的工程中导入依赖
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>9.4.0</version>
</dependency>
(2)、修改Account接口
在Account接口中获取traceId并添加自定义信息
@RestController
@RequestMapping("/account")
public class AccountController {
@Autowired
private AccountService accountService;
@RequestMapping("/getAccount")
public String getAccount() {
System.out.println("AccountController getAccount 执行");
// 获取traceId、segmentId、spanId
String traceId = TraceContext.traceId();
String segmentId = TraceContext.segmentId();
int spanId = TraceContext.spanId();
System.out.println("traceId: " + traceId + ", segmentId: " + segmentId + ", spanId: " + spanId);
// 添加自定义信息
TraceContext.putCorrelation("message","测试");
accountService.getAccount();
return "success";
}
}

(3)、修改Product接口
在Product接口中获取获取traceId、segmentId、spanId及自定义信息
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/getProduct")
public void getProduct() {
// 获取traceId、segmentId、spanId
String traceId = TraceContext.traceId();
String segmentId = TraceContext.segmentId();
int spanId = TraceContext.spanId();
System.out.println("traceId: " + traceId + ", segmentId: " + segmentId + ", spanId: " + spanId);
// 添加自定义信息
Optional<String> message = TraceContext.getCorrelation("message");
System.out.println("mmessage:+mmessage");
productService.getProduct();
}
}

(4)、测试访问
启动项目,访问http://localhost:8081/account/getAccount
-
查看Account日志

-
查看Product日志

1.4.2、@Trace
默认情况下 Skywalking 仅会对服务间的远程调用进行跟踪,对于本地调用是不进行跟踪的,即本地调用过程在 Skywalking 的 UI 控制台是看不到的。通过在本地方法上添加@Trace,可将该方法纳入到 Skywalking 的跟踪范围,从 UI 控制台可看到其调用过程。不过需要注意的是,该注解不能用于静态方法。
(1)、引入依赖
在需要使用 Tracing API 的工程中导入依赖
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>9.4.0</version>
</dependency>
(2)、添加测试方法
修改Account项目,添加方法内部调用
@RestController
@RequestMapping("/account")
public class AccountController {
@Autowired
private AccountService accountService;
@RequestMapping("/getAccount")
public String getAccount() {
System.out.println("AccountController getAccount 执行");
accountService.getAccount();
testAccount();
return "success";
}
public void testAccount() {
System.out.println("AccountController testAccount");
testBccount();
}
public void testBccount() {
System.out.println("AccountController testBccount");
testCccount();
}
public void testCccount() {
System.out.println("AccountController testCccount");
}
}
(3)、测试追踪
启动项目访问http://localhost:8081/account/getAccount
查看追踪链

(4)、添加@Trace注解
在需要监控的方法上添加@Trace注解
@RestController
@RequestMapping("/account")
public class AccountController {
@Autowired
private AccountService accountService;
@RequestMapping("/getAccount")
public String getAccount() {
System.out.println("AccountController getAccount 执行");
accountService.getAccount();
testAccount();
return "success";
}
@Trace
public void testAccount() {
System.out.println("AccountController testAccount");
testBccount();
}
public void testBccount() {
System.out.println("AccountController testBccount");
testCccount();
}
@Trace
public void testCccount() {
System.out.println("AccountController testCccount");
}
}
(5)、查看追踪链
启动项目访问http://localhost:8081/account/getAccount

5、告警功能
Skywalking 具有告警功能,即用户可提前设置好告警规则,一旦调用过程出现符合告警规则的调用,则会触发相应的 Webhooks 的执行,并在 UI 控制台的"告警"页面中显示相应的告警信息,以通知管理员目前出现的问题
注意:告警规则会有一定的延时
1.5.1、告警规则简介
告警规则可以查看https://github.com/apache/skywalking官网介绍

进入/docs/en/setup/backend目录,查看backend-alarm.md文档

(1)、服务响应超时告警
这个规则针对的是某个微服务的所有实例
-
规则1
默认在最近十分钟内的三分钟时间里,如果微服务的响应超过1000毫秒则报警提示,报警时间间隔5分钟
rules: service_resp_time_rule: # 告警规则是最近的3分钟内如果微服务的响应时间超过1000毫秒则告警提示 expression: sum(service_resp_time > 1000) >= 3 # 统计时长是10分钟 period: 10 # 告警后沉默的时间间隔是5分钟,也就是告警后5分钟内有相同的告警则不再提示,超过5分钟后的告警才会再次提示 # 如果不设置则使用period设置的时间间隔 silence-period: 5 message: Response time of service {name} is more than 1000ms in 3 minutes of last 10 minutes. -
规则2
默认是最近10分钟内微服务的平均响应时长超过1000毫秒则告警提示
rules: service_resp_time_rule: # 告警规则是最近10分钟内微服务的平均响应时长超过1000毫秒则告警提示 expression: avg(service_resp_time) > 1000 # 统计时长是10分钟 period: 10 # 告警后沉默的时间间隔是5分钟,也就是告警后5分钟内有相同的告警则不再提示,超过5分钟后的告警才会再次提示 # 如果不设置则使用period设置的时间间隔 silence-period: 5 message: Avg response time of service {name} is more than 1000ms in last 10 minutes.
(2)、成功率告警
默认规则是最近的10分钟内的2分钟里如果微服务的响应成功率低于百分之80则告警
rules:
service_sla_rule:
# 最近的10分钟内的2分钟里如果微服务的响应成功率低于百分之80则告警
expression: sum(service_sla < 8000) >= 2
# 统计时长是10分钟
period: 10
# 告警后沉默的时间间隔是3分钟,也就是告警后3分钟内有相同的告警则不再提示,超过3分钟后的告警才会再次提示
# 如果不设置则使用period设置的时间间隔
silence-period: 3
message: Successful rate of service {name} is lower than 80% in 2 minutes of last 10 minutes
(3)、响应百分比告警
默认规则是在最近10分钟的3分钟里,如果微服务的响应百分比出现以下情况则告警
- 百分之50的响应时长大于1秒
- 百分之75的响应时长大于1秒
- 百分之90的响应时长大于1秒
- 百分之95的响应时长大于1秒
- 百分之99的响应时长大于1秒
rules:
service_resp_time_percentile_rule:
# 告警规则
expression: sum(service_percentile{p='50,75,90,95,99'} > 1000) >= 3
# 统计时长10分钟
period: 10
# 告警后沉默的时间间隔是5分钟,也就是告警后5分钟内有相同的告警则不再提示,超过5分钟后的告警才会再次提示
# 如果不设置则使用period设置的时间间隔
silence-period: 5
message: Percentile response time of service {name} alarm in 3 minutes of last 10 minutes, due to more than one condition of p50 > 1000, p75 > 1000, p90 > 1000, p95 > 1000, p99 > 1000
(4)、服务响应超时告警
这个规则针对的是某个微服务的某个实例
rules:
service_instance_resp_time_rule:
# 告警规则是最近的2分钟内如果微服务的响应时间超过1000毫秒则告警提示
expression: sum(service_instance_resp_time > 1000) >= 2
# 统计时长10分钟
period: 10
# 告警后沉默的时间间隔是5分钟,也就是告警后5分钟内有相同的告警则不再提示,超过5分钟后的告警才会再次提示
# 如果不设置则使用period设置的时间间隔
silence-period: 5
message: Response time of service instance {name} is more than 1000ms in 2 minutes of last 10 minutes
(5)、数据库超时告警
默认规则是最近10分钟的2分钟内如果数据库响应超过1000毫秒则告警提示
rules:
database_access_resp_time_rule:
# 默认规则是最近10分钟的2分钟内如果数据库响应超过1000毫秒则告警提示
expression: sum(database_access_resp_time > 1000) >= 2
# 统计时长10分钟
# 告警后沉默的时间间隔是10分钟,也就是告警后10分钟内有相同的告警则不再提示,超过10分钟后的告警才会再次提示
# 这里没有设置silence-period则使用period设置的时间间隔
period: 10
message: Response time of database access {name} is more than 1000ms in 2 minutes of last 10 minutes
(6)、端点映射响应超时告警
默认规则是在过去10分钟的2分钟内,如果端点的响应时长超过1000毫秒则告警
端点可以理解为微服务的服务接口
rules:
endpoint_relation_resp_time_rule:
expression: sum(endpoint_relation_resp_time > 1000) >= 2
# 统计时长10分钟
# 告警后沉默的时间间隔是10分钟,也就是告警后10分钟内有相同的告警则不再提示,超过10分钟后的告警才会再次提示
# 这里没有设置silence-period则使用period设置的时间间隔
period: 10
message: Response time of endpoint relation {name} is more than 1000ms in 2 minutes of last 10 minutes
(7)、端点响应超时告警
默认规则是在过去10分钟的2分钟内,如果端点的响应时长超过1000毫秒则告警
端点可以理解为微服务的服务接口
rules:
endpoint_resp_time_rule:
expression: sum(endpoint_resp_time > 1000) >= 2
# 统计时长10分钟
period: 10
# 告警后沉默的时间间隔是5分钟,也就是告警后5分钟内有相同的告警则不再提示,超过5分钟后的告警才会再次提示
# 如果不设置则使用period设置的时间间隔
silence-period: 5
message: Response time of endpoint {name} is more than 1000ms in 2 minutes of last 10 minutes
1.5.2、测试告警规则
(1)、修改规则
修改 Skywalking服务端/config/alarm-settings.yml 文件内容
这里测试响应时长告警,修改service_resp_time_rule规则如下,将时长设置为1分钟,沉默时长也设置为1分钟
rules:
service_resp_time_rule:
expression: sum(service_resp_time > 1000) >= 1
period: 1
silence-period: 1
message: Response time of service {name} is more than 1000ms in 1 minutes of last 10 minutes.
(2)、修改回调接口
修改 Skywalking服务端/config/alarm-settings.yml 文件内容

(3)、添加回调接口
在AccountController 类中添加 notify()方法
该方法是告警的 webhooks 方法。该方法原本应该定义在专门的一个工程中,为了简单,就定义在了这里。
当发生符合告警规则的调用时, skywalking 会向 webhooks 提交一个 POST 请求,并携带着告警相关的信息
@RestController
@RequestMapping("/account")
public class AccountController {
@Autowired
private AccountService accountService;
@RequestMapping("/getAccount")
public String getAccount() {
System.out.println("AccountController getAccount 执行");
accountService.getAccount();
return "success";
}
/**
* 添加告警回调方法
* @param notify
*/
@PostMapping("/notify")
public void notify(Object notify) {
System.out.println("AccountController notify notify: " + notify);
}
}
(4)、修改Product接口
在测试方法中添加沉睡代码,模拟响应超时
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/getProduct")
public void getProduct() throws InterruptedException {
//TimeUnit.MICROSECONDS.sleep(1500);
Thread.sleep(50000);
productService.getProduct();
}
}
(5)、重启Server
Server的配置修改后需要重启服务
(6)、访问测试
快速访问http://localhost:8081/account/getAccount
查看http://localhost:9999/alerting

二、Sleuth+zipkin
1、Sleuth简介
Spring Cloud Sleuth 的官方文档地址:https://spring.io/projects/spring-cloud-sleuth#support
2.1.1、Sleuth 简介
Sleuth是Spring cloud的分布式跟踪解决方案
是 Spring Cloud 提供的分布式链路追踪库,它会在每个请求中自动生成 Trace ID 和 Span ID ,并将这些 ID 传递到调用链中的所有服务中,确保请求的追踪信息在各个微服务之间的传递。
特点:1.提供链路追踪 2.性能分析 3.数据分析 4.可视化
注意:
- Spring Cloud Sleuth不适用于Spring Boot 3.x以后的版本
- Sleuth支持的Spring Boot的最后一个主要版本是2.x

2.1.2、Sleuth 的术语
Spring Cloud Sleuth采用的是Google的开源项目Dapper的专业术语
包括了
-
Span:跨度,表示一次调用的过程,一次跟踪包含多次调用过程
-
Treace:表示整个追踪过程,从用户发起请求到最终的响应
-
Annotation:用来及时记录一个事件的存在,一些核心Annotations用来定义一个请求的开始和结束
Annotation主要包括以下几个事件标识
- cs: Client Sent, 表示客户端发送了请求,这个标识意味着跨度的开始
- sr: Server Received,表示服务端接收到请求,并开始进行处理
- ss: Server Sent,表示服务器端完成请求的处理,并对客户端做出响应
- cr: Client Received, 表示客户端接收到响应,意味着整个跨度的结束
2.1.3、主要功能
- 传播追踪上下文:在服务调用间传递Trace ID和Span ID,确保整个请求链路的追踪信息保持一致
- 集成日志框架:修改日志框架的配置,使得日志记录中包含Trace和Span的ID
- 与Spring Cloud生态集成:与Spring Cloud的其他组件(如Ribbon、Hystrix、Zuul等)无缝集成
- 依赖性:Sleuth是为Spring Cloud应用程序设计的,它依赖于Spring框架和Spring Boot
2、zipkin介绍
zipkin 官网https://zipkin.io/pages/quickstart.html
2.2.1、zipkin简介
是一种分布式追踪系统,支持收集和展示 Spring Cloud Sleuth 生成的追踪数据。
它能够将每个请求详细路径进行可视化展示,便于开发者分析和排查问题。
主要功能
- 收集追踪数据:Zipkin通过其客户端库(如Brave)收集追踪数据。
- 存储追踪数据:支持多种存储方案,如内存、MySQL、Cassandra、Elasticsearch等。
- 查询和展示:提供Web界面,用于查询追踪数据,并以可视化的方式展示服务间的调用关系和延迟。
- 独立性:Zipkin可以独立于任何应用程序或框架运行,并且可以与多种编程语言和框架集成。
2.2.2、Zipkin与Sleuth的协作
- 数据收集:Sleuth负责在Spring Cloud应用程序中生成和传播追踪数据,而Zipkin负责收集这些数据。
- 数据展示:Sleuth生成的追踪数据可以通过Zipkin的Web界面进行查询和展示。
- 集成:在Spring Cloud应用程序中,Sleuth通常与Zipkin一起使用,Sleuth负责追踪信息的生成和传播,Zipkin负责存储和展示。
2.2.3、Sleuth和 Zipkin的关系图

3、环境搭建
2.3.1、安装zipkin
安装 Sleuth 的官网网址:https://repo1.maven.org/maven2/io/zipkin/java/zipkin-server/2.12.9/

下载得到 : zipkin-server-2.12.9-exec.jar
把 zipkin-server-2.12.9-exec.jar 放到指定的目录 , 比如H:\ZipkinServer

在该(H:\ZipkinServer)目录下,进入 cmd , 执行如下指令
java -jar zipkin-server-2.12.9-exec.jar

浏览器输入:http://localhost:9411/zipkin/
注意:不要关闭命令窗口,不要关闭 刚刚运行的ziplin程序

2.3.2、搭建工程
这里搭建Account工程和Product工程,在Account工程中调用Product工程
2.3.2.1、搭建Account工程
(1)、POM依赖
引入sleuth和zipkin的依赖
<?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.seata</groupId>
<artifactId>SleuthZipkinDemo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.account</groupId>
<artifactId>account-service</artifactId>
<dependencies>
<!--feign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.9</version>
</dependency>
<!--负载均衡依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<version>3.1.9</version>
</dependency>
<!-- web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos 服务注册-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!--sleuth依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<version>3.1.11</version>
</dependency>
<!--zipkin依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
<version>3.1.11</version>
</dependency>
</dependencies>
</project>
(2)、配置文件
添加zipkin和sleuth的配置
##指定本服务的端口号
server:
port: 8081
spring:
#设置当前应用的名称
application:
name: account-service
zipkin:
#zipkin服务所在地址
base-url: http://127.0.0.1:9411/
sender:
type: web #使用http的方式传输数据
#配置采样百分比
sleuth:
sampler:
probability: 1 # 将采样比例设置为 1.0,也就是全部都需要。默认是0.1也就是10%,一般情况下,10%就够用了
#数据库连接配置
datasource:
url: jdbc:mysql://192.168.1.110:3306/nacos?characterEncoding=UTF-8&useSSL=false&useUnicode=true&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
cloud:
# nacos相关的配置
nacos:
discovery:
# 配置注册中心地址
server-addr: 192.168.1.110:8848
# 设置连接nacos的用户名、密码
username: nacos
password: nacos
(3)、测试接口
package com.account.controller;
import com.account.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/account")
public class AccountController {
@Autowired
private AccountService accountService;
@RequestMapping("/getAccount")
public String getAccount() {
System.out.println("AccountController getAccount 执行");
accountService.getAccount();
return "success";
}
}
(4)、测试Service
package com.account.service;
import com.account.client.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccountService {
@Autowired
ProductService productService;
public void getAccount() {
System.out.println("AccountService getAccount 执行");
productService.getProduct();
}
}
(5)、Feign配置类
配置重试次数
package com.account.config;
import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* 自定义Feign配置类
*/
@Configuration
public class FeignConfig {
@Bean
public Retryer feignRetryer() {
return new Retryer.Default(100, SECONDS.toMillis(1), 5);
}
}
(6)、Feign客户端
远程调用product工程
package com.account.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 用于访问Product服务的feign
*/
@FeignClient("product-service")
public interface ProductService {
@RequestMapping(value = "/product/getProduct")
String getProduct();
}
(7)、启动类
开启Feign功能
package com.account;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class AccountApplication {
public static void main(String[] args) {
SpringApplication.run(AccountApplication.class, args);
}
}
2.3.2.2、搭建Product工程
(1)、POM依赖
引入zipkin和sleuth依赖
<?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.seata</groupId>
<artifactId>SleuthZipkinDemo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.product</groupId>
<artifactId>product-service</artifactId>
<dependencies>
<!-- web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos 服务注册-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!--sleuth依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<version>3.1.11</version>
</dependency>
<!--zipkin依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
<version>3.1.11</version>
</dependency>
</dependencies>
</project>
(2)、配置文件
添加zipkin和sleuth的配置
##指定本服务的端口号
server:
port: 8082
spring:
#设置当前应用的名称
application:
name: product-service
zipkin:
#zipkin服务所在地址
base-url: http://127.0.0.1:9411/
sender:
type: web #使用http的方式传输数据
#配置采样百分比
sleuth:
sampler:
probability: 1 # 将采样比例设置为 1.0,也就是全部都需要。默认是0.1也就是10%,一般情况下,10%就够用了
#数据库连接配置
datasource:
url: jdbc:mysql://192.168.1.110:3306/nacos?characterEncoding=UTF-8&useSSL=false&useUnicode=true&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
cloud:
# nacos相关的配置
nacos:
discovery:
# 配置注册中心地址
server-addr: 192.168.1.110:8848
# 设置连接nacos的用户名、密码
username: nacos
password: nacos
(3)、测试接口
package com.product.controller;
import com.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/getProduct")
public void getProduct() throws InterruptedException {
productService.getProduct();
}
}
(4)、测试Service
package com.product.service;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
public void getProduct() {
System.out.println("ProductService getProduct 执行");
}
}
2.3.3、测试链路
-
启动项目,访问http://localhost:8081/account/getAccount
-
查看zipkin信息

2.3.4、使用总结
-
第一步
安装zipkinServer并启动
-
第二步
引入zipkin和Sleuth依赖
-
第三步
添加zipkin和Sleuth配置信息
更多推荐


所有评论(0)