Apache SkyWalking
布式架构下的排查问题复杂性
分布式应用架构的运维和诊断的过程变得越来越复杂,例如:接口诊断困难、应用性能诊断复杂、架构分析复杂等难题,传统的监控工具并无法满足,分布式链路系统由此诞生
核心:将一次请求分布式调用,使用 GPS 定位串起来,记录每个调用的耗时、性能等日志,并通过可视化工具展示出来
APM 系统(Application Performance Management)
- 讲到分布式的链路追踪系统,不得不说说它的前世今生了,APM 系统是可以帮助系统的行为做性能分析的工具
- APM 系统,它是由谷歌公开的论文提到的,可以说谷歌还是很强的!而到后面,许多的技术公司就基于这边论文的原理,开发出来很多出色的 APM 框架,比如:skywalking、zipkin 等等
SkyWalking
-
Skywalking 是什么
- skywalkings 是一款国产的开源框架,在 2015 年开源使用,在 2017 年的时候加入了 Apache 孵化器
- skywalking 是分布式应用程序的性能监控工具,专门是为了微服务(Spring cloud)、云原生架构与容器架构(docker/k8s)而设计的
- 是一款 APM 工具,它具有分布式追踪、性能指标分析、应用和服务依赖分析等功能
- 官网:http://skywalking.apache.org/
- 下载:http://skywalking.apache.org/downloads/
- GitHub:https://github.com/apache/skywalking
- 官方文档:https://skywalking.apache.org/docs/main/v8.5.0/readme/
- 中文文档:https://skyapm.github.io/document-cn-translation-of-skywalking/
-
市场上同类解决方案
- Zipkin 是由 Twitter 开源的链路分析分析工具,在 SpringCloud sleuth 得到了广泛的使用,具有轻量,部署简单的特点
- Pinpoint 是由韩国人开发的链路追踪应用监控分析工具,基于字节码方式注入。具有支持多种插件,UI 功能强大,接入端没有代码侵入
- Skywalking 是由国人开发的链路追踪应用监控分析工具,基于字节码方式注入。具有支持多种插件,UI 功能强大,接入端没有代码侵入,现已加入 Apache 孵化器
- CAT 是大众点评开源的链路追踪分析工具,具有对应用监控的分析、日志的采集、监控报警一系列的监控平台
项目 Cat Zipkin Skywalking 链路追踪可视化 是 是 是 聚合报表 优 良好 中等 服务的依赖图 良好 良好 优 埋点方式 入侵式 入侵式 非入侵式,字节码增强 VM 监控指标 有 没有 有 存储机制 MySQL、本地文件 内存、es、MySQL、等 H2、es、MySQL、TIDB 等 支持语言 java/.net 丰富 丰富 社区支持 国内 国外主流 apache 支持 知名使用者 美团、携程 京东、阿里定制不开源 华为、小米 APM 是 否 是 开发基础 eBay cal Google Dapper Google Dapper 支持 webflux 不支持 支持 支持 GitHub 下载量 12.3k 12.2k 19.3k
Apache Skywalking 特点和整体架构组件
-
Skywalking 特点
- 具有多种监控手段,可以通过语言探针来获取监控数据
- 具有多种语言的自动探针。它包括了 Java、.net、node.js 等
- 具有轻量有高效的特点,不占用大量的服务器资源
- 清晰的模块化,UI、存储、集群管理都有许多种机制供选择
- 支持告警,具有优秀的可视化解决方案
- 可以在多种环境下运行,例如:像注册中心,Eureka 和 RPC 框架 SpringCloud dubbo
-
Skywalking 整体架构
- 可以分为:上、下、左、右四个部分
- 上部分(skywalking-agent):这一部分负责从应用程序中收集链路信息,然后把链路信息发送给 skywalking OAP 处理器
- 下部分(skywalking OAp):负责接收从 skywalking-agent 发送过来的 Tracing 数据信息,然后把数据信息给 Analysis Core 进行分析,把分析到的数据存储到外部的存储器当中,最后面把数据信息给 Query Core 提供查询数据的功能
- 左部分(Skywalking UI):负责给用户查看链路等信息
- 部署组件介绍
- 数据存储(H2/mysql/ElasticSearch)
- Skywalking-OAP-Server
- Skywalking UI
- Skywalking-Agent(项目引入)
阿里云 docker 安装
1# 1.先安装yml
2yum install -y yum-utils device-mapper-persistent-data lvm2
3
4# 2.设置阿里云镜像
5sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
6
7# 3.查看可安装的docker版本
8yum list docker-ce --showduplicates | sort -r
9
10#4. 安装docker
11yum -y install docker-ce-20.10.10-3.el7
12
13#5. 查看docker版本
14docker -v
15
16#6. 启动docker
17systemctl start docker
18
19#7. 查看docker 启动状态
20systemctl status docker
21
22#查看端口占用命令安装
23yum install -y lsof
Skywalking 部署:数据存储 ElasticSearch
- Elasticsearch 是⼀个开源,是⼀个基于 Apache Lucene 库构建的 RESTFul 搜索引擎. Elasticsearch 是在 Solr 之后⼏年推出的。
- 它提供了⼀个分布式,多租户能⼒的全⽂搜索引擎,具有 HTTP Web 界⾯(REST)和⽆架构 JSON ⽂档
- Elasticsearch 的官⽅客户端库提供 Java,Groovy,PHP,Ruby,Perl,Python,.NET 和 JavaScript。
docker 部署 ES7
1# 创建配置文件目录、数据持久化目录
2[root@localhost ~]# mkdir -p /mydata/es/config
3[root@localhost ~]# mkdir -p /mydata/es/data
4[root@localhost ~]# chmod 777 -R /mydata/es
5
6# 创建配置文件并开启远程连接
7[root@localhost ~]# echo "http.host: 0.0.0.0" >> /mydata/es/config/elasticsearch.yml
8
9# 启动运行
10docker run -d --name xdclass_es7 -p 9200:9200 -p 9300:9300 \
11 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
12 -v /mydata/es/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
13 -v /mydata/es/data:/usr/share/elasticsearch/data \
14 -v /mydata/es/plugins:/usr/share/elasticsearch/plugins elasticsearch:7.6.2
15
16参数说明
17-e "discovery.type=single-node" 设置为单节点
18-e ES_JAVA_OPTS="-Xms512m -Xmx512m" 设置ES的初始内存和最大内存,否则过大启动不了ES
19
20注意:这时候会提示权限不够需要开启权限:chmod 777 -R /mydata/es
21 登录http://ip:9200/_cat/nodes?v=true&pretty
22 记得开放网络安全组 9200 9300
23
24# 访问验证
25http://192.168.10.25:9200/_cat/nodes?v=true&pretty
Skywalking 部署:数据存储 OAP-Server
1docker run --name xd_oap --restart always -d -e TZ=Asia/Shanghai -p 12800:12800 -p 11800:11800 --link xdclass_es7 -e SW_STORAGE=elasticsearch7 -e SW_STORAGE_ES_CLUSTER_NODES=xdclass_es7:9200 apache/skywalking-oap-server:8.5.0-es7
2
3参数:
4--link <name or id>:alias ,添加到另一个容器的链接,可以添加别名或者不加
5–link后面的参数和elasticsearch容器名一致;
6SW_STORAGE=elasticsearch7 是固定的,使用es7;
7SW_STORAGE_ES_CLUSTER_NODES:xdclass_es7也可改为es服务器部署的Ip地址,比如ip:9200
Skywalking 部署:数据存储 UI
1# 安装ui
2docker run -d --name xdclass_skywalking-ui \
3--restart=always \
4-e TZ=Asia/Shanghai \
5-p 8080:8080 \
6--link xd_oap \
7-e SW_OAP_ADDRESS=xd_oap:12800 \
8apache/skywalking-ui:8.5.0
9
10# SkyWalking UI界面访问地址
11http://192.168.10.25:8080/
12
13# 查看ElasticSearch全部索引
14http://192.168.10.25:9200/_cat/indices?v=true&pretty
Apache Skywalking 常见概念和指标
-
服务(Service)
- 比如商品微服务
-
实例(Instance)
- 比如 机器 1(192.12.41.4)
-
端点(Endpoint)
- 比如商品微服务对外提供的接口 /api/v1/product/list,就是端点
-
用户的满意程度 Service Apdex
-
全称 Application Performance Index,最大值就是 1, 是一个不断优化的方向
-
分 3 个指标,T 值代表着用户对应用性能满意的响应时间界限或者说是“门槛”,假如 T 是 0.5 秒
- 满意:这样的响应时间让用户感到很愉快,响应时间少于 T 秒钟; 0.5 秒内
- 容忍:慢了一点,但还可以接受,继续这一应用过程,响应时间 T~4T 秒; 0.5~2 秒内
- 失望:太慢了,受不了了,用户决定放弃这个应用,响应时间超过 4T 秒; 多于 2 秒
-
-
SLA
- 服务等级协议,全称:service level agreement,为保障服务的性能和可用性,
- 9 越多代表全年服务可用时间越长服务更可靠,停机时间越短
11年 = 365天 = 8760小时
2
399.9 = 8760 * 0.1% = 8760 * 0.001 = 8.76小时
4
599.99 = 8760 * 0.0001 = 0.876小时 = 0.876 * 60 = 52.6分钟
6
799.999 = 8760 * 0.00001 = 0.0876小时 = 0.0876 * 60 = 5.26分钟
8
9从以上看来,全年停机5.26分钟才能做到99.999%,即5个9
-
CPM
- 全称 call per minutes,是吞吐量(Throughput)指标,每分钟请求调用的次数
-
RT
- Response Time 表示请求响应时间,对于人来说,响应时间最好不要超过 2 秒
-
Percent Response 百分位数统计
- 表示采集样本中某些值的占比,Skywalking 有 “p50、p75、p90、p95、p99” 一些列值
- 比如 “p99:360” 表示 99% 请求的响应时间在 360ms 以内
Skywalking RocketBot 整体界面的介绍
Skywalking ui 控制栏
- 仪表盘:查看被监控服务的运行状态
- 拓扑图:以拓扑图的方式展现服务的关系
- 追踪:以接口的列表方式展现
- 性能剖析:对端点进行采样分析
- 日志:可查看服务日志
- 告警:触发告警的告警列表,包括了服务的失败率,超时等待
展示栏(Global 全局维度)
- Global、Service、Instance、Endpoint 不同展示面板
- Services load:服务每分钟请求数
- Slow Services:慢响应服务,单位 ms
- Un-Health services(Apdex): Apdex 性能指标,1 为满分
- Slow Endpoint:慢响应端点,单位 ms
- Global Response Latency:百分比响应延时,不同百分比的延时时间,单位 ms
- Global Heatmap:服务响应时间热力分布图,根据时间段内不同响应时间的数量显示颜色深度;
- 底部栏:展示数据的时间区间,点击可以调整
展示栏(Service 服务维度)
- Service Apdex(数字):当前服务的评分
- Service Apdex(折线图):不同时间的 Apdex 评分
- Service Avg Response Times:平均响应延时,单位 ms
- Service Response Time Percentile:百分比响应延时
- Successful Rate(数字):请求成功率
- Successful Rate(折线图):不同时间的请求成功率
- Servce Load(数字):每分钟请求数
- Servce Load(折线图):不同时间的每分钟请求数
- Servce Instances Load:每个服务实例的每分钟请求数
展示栏(Instance 服务维度,不过对于监控 CPU、内存等,Promethus 是个更好的选择)
- Service instance load:当前实例的每分钟请求数
- Service Instance Successful Rate:当前实例的请求成功率
- Service Instance Latency:当前实例的响应延时
- JVM CPU:jvm 占用 CPU 的百分比
- JVM Memory:JVM 内存占用大小,单位 m
- JVM GC Time:JVM 垃圾回收时间,包含 YGC 和 OGC
- JVM GC Count:JVM 垃圾回收次数,包含 YGC 和 OGC
- JVM Thread Count:JVM 线程数
追踪
- 左侧:接口列表,请求为红色表示异常,蓝色表示正常
- 右侧:追踪列表,API 的各个连接点按照端点的先后顺序和时间排序
性能剖析
- 新建任务:新建需要分析的端点
- 左侧列表:对任务进行采样
- 右侧:每个端点的链路信息
SpringBoot 整合分布式链路追踪 SkyWalking
必须是 JDK11,不然探针将失效
jdk-11.0.1
apache-skywalking-apm-8.5.0
SQL 文件:shop.sql
源码:xdclass-sky.zip
1### 数据库名字:xdclass_sky_shop
2SET NAMES utf8mb4;
3SET FOREIGN_KEY_CHECKS = 0;
4
5-- ----------------------------
6-- Table structure for product
7-- ----------------------------
8DROP TABLE IF EXISTS `product`;
9CREATE TABLE `product` (
10 `id` bigint NOT NULL,
11 `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '商品标题',
12 `detail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '详情',
13 `img` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '图片',
14 `amount` decimal(16,0) DEFAULT NULL COMMENT '现价',
15 PRIMARY KEY (`id`)
16) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
17
18-- ----------------------------
19-- Records of product
20-- ----------------------------
21BEGIN;
22INSERT INTO `product` VALUES (1, '商用短链平台-海量数据项目大课', '打造最佳简历项目的,短链平台大课项目', 'https://file.xdclass.net/video/2022/71-HLSJCL/05.jpeg', 66);
23INSERT INTO `product` VALUES (2, '新一代AlibabaCloud全家桶实战', '高并发分布式项目', 'https://file.xdclass.net/video/2022/71-HLSJCL/05.jpeg', 88);
24INSERT INTO `product` VALUES (3, '小滴课堂永久会员', '观看全部IT专题视频,永久有效', 'https://file.xdclass.net/video/2022/banner/03.jpeg', 999);
25INSERT INTO `product` VALUES (4, '架构大课训练营', '全方位提升综合能力,存储、网络、架构、解决方案', 'https://file.xdclass.net/video/2022/72-ShardingJDBC/banner.jpeg', 79);
26COMMIT;
27
28SET FOREIGN_KEY_CHECKS = 1;
引入依赖
1<dependency>
2 <groupId>mysql</groupId>
3 <artifactId>mysql-connector-java</artifactId>
4 <version>8.0.26</version>
5 </dependency>
6
7 <dependency>
8 <groupId>com.baomidou</groupId>
9 <artifactId>mybatis-plus-boot-starter</artifactId>
10 <version>3.4.1</version>
11 </dependency>
application.properties
1server.port=8081
2#==============================???????========================================
3spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver
4spring.datasource.url=jdbc:mysql://192.168.10.25:3306/xdclass_sky_shop?useUnicode=true&characterEncoding=utf-8&useSSL=false&&allowPublicKeyRetrieval=true
5spring.datasource.username =root
6spring.datasource.password =123456
开发接口
- 商品列表接口
- 商品详情接口
model
1package net.xdclass.xdclasssky.model;
2
3import com.baomidou.mybatisplus.annotation.TableName;
4import lombok.Data;
5
6import java.math.BigDecimal;
7
8@TableName("product")
9@Data
10public class ProductDO {
11
12 private Long id;
13
14 /**
15 * 商品标题
16 */
17 private String title;
18
19 /**
20 * 详情
21 */
22 private String detail;
23
24 /**
25 * 图片
26 */
27 private String img;
28
29 /**
30 * 现价
31 */
32 private BigDecimal amount;
33
34 // set、get方法
35}
model:工具类 jsondata
1package net.xdclass.xdclasssky.model;
2
3import lombok.AllArgsConstructor;
4import lombok.Data;
5import lombok.NoArgsConstructor;
6
7@Data
8@NoArgsConstructor
9@AllArgsConstructor
10public class JsonData {
11 private int code;
12 private Object object;
13 private String msg;
14
15 public static JsonData buildSuccess(Object data) {
16 return new JsonData(0, data, "");
17 }
18}
mapper
1package net.xdclass.xdclasssky.mapper;
2
3import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4import net.xdclass.xdclasssky.model.ProductDO;
5
6public interface ProductMapper extends BaseMapper<ProductDO> {
7}
service:interface
1package net.xdclass.xdclasssky.mapper;
2
3import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4import net.xdclass.xdclasssky.model.ProductDO;
5
6public interface ProductMapper extends BaseMapper<ProductDO> {
7}
service:Impl
1package net.xdclass.xdclasssky.service.impl;
2
3import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
4import net.xdclass.xdclasssky.mapper.ProductMapper;
5import net.xdclass.xdclasssky.model.ProductDO;
6import net.xdclass.xdclasssky.service.ProductService;
7import org.springframework.beans.factory.annotation.Autowired;
8import org.springframework.stereotype.Service;
9
10import java.util.List;
11
12@Service
13public class ProductServiceImpl implements ProductService {
14
15 @Autowired
16 private ProductMapper productMapper;
17
18 @Override
19 public List<ProductDO> list() {
20 return productMapper.selectList(new QueryWrapper<>());
21 }
22
23 @Override
24 public ProductDO findDetailById(long productId) {
25 return productMapper.selectOne(new QueryWrapper<ProductDO>().eq("id",productId));
26 }
27
28
29}
controller
1package net.xdclass.xdclasssky.controller;
2
3import net.xdclass.xdclasssky.model.JsonData;
4import net.xdclass.xdclasssky.model.ProductDO;
5import net.xdclass.xdclasssky.service.ProductService;
6import org.springframework.beans.factory.annotation.Autowired;
7import org.springframework.web.bind.annotation.GetMapping;
8import org.springframework.web.bind.annotation.PathVariable;
9import org.springframework.web.bind.annotation.RequestMapping;
10import org.springframework.web.bind.annotation.RestController;
11
12import java.util.List;
13
14@RestController
15@RequestMapping("/api/product/v1")
16public class ProductController {
17
18 @Autowired
19 private ProductService productService;
20
21 /**
22 * 查看商品列表接口
23 *
24 * @return
25 */
26 @GetMapping("list")
27 public JsonData list() {
28
29 List<ProductDO> list = productService.list();
30 return JsonData.buildSuccess(list);
31 }
32
33 /**
34 * 查看商品详情
35 *
36 * @param productId
37 * @return
38 */
39 @GetMapping("/detail/{product_id}")
40 public JsonData detail(@PathVariable("product_id") long productId) {
41 ProductDO productDO = productService.findDetailById(productId);
42 return JsonData.buildSuccess(productDO);
43 }
44}
XdclassSkyApplication
1package net.xdclass.xdclasssky;
2
3import org.mybatis.spring.annotation.MapperScan;
4import org.springframework.boot.SpringApplication;
5import org.springframework.boot.autoconfigure.SpringBootApplication;
6
7@SpringBootApplication
8@MapperScan("net.xdclass.xdclasssky.mapper")
9public class XdclassSkyApplication {
10
11 public static void main(String[] args) {
12 SpringApplication.run(XdclassSkyApplication.class, args);
13 }
14
15}
测试接口 API
1http://192.168.10.88:8081/api/product/v1/list
2http://192.168.10.88:8081/api/product/v1/detail/1
Skywalking Agent(探针)
-
什么 SkyWalking 探针
- 探针表示集成到目标系统中的代理或 SDK 库,即收集并格式化数据, 并发送到后端 包括链路追踪和性能指标
-
Skywalking-Agent 的安装和使用
- 下载 skywalking
skywalking-agen 目录文件的介绍
- logs:skywalking agent 的相关运行日志
- bootstrap-plugins:插件包
- optional-plugins:插件包(可供选择的插件包,如果需要生效则需要拷贝到 plugins 包下)
- plugins:插件包(生效的插件包,支持多个框架链路追踪)
- optional-reporter-plugins:插件包
- activations:插件包
- config: 配置文件
- skywalking-agent.jar:agent 代理的 jar 包(主要是这个!)
skywalking-agent 的使用方式
- 优先级:探针-> JVM 配置-> 环境变量配置 -> agent.config(优先级低)
- 第一种-javaagent:/path/to/skywalking-agent.jar={config1}={value1},{config2}={value2}
1-javaagent:../skywalking-agent.jar=agent.service_name=XdclassApp,collector.backend_service=127.0.0.1:11800
- 第二种:-Dskywalking.[option1]=[value2]
1-javaagent: ../skywalking-agent.jar -Dskywalking.agent.service_name=XdclassApp -Dskywalking.collector.backend_service=127.0.0.1:11800
- agent.service_name:客户端服务名,在 apm 系统中显示的服务名称
- collector.backend_service:SW 上传的服务地址
IDEA 编辑器配置 Skywalking-Agent 链路采集
-
agent.service_name:客户端服务名,在 apm 系统中显示的服务名称
-
collector.backend_service:SW AOPServer 的地址上传的服务地址
1
路径中不要出现中文目录
-javaagent:D:\Project\agent\skywalking-agent.jar -Dskywalking.agent.service_name=XdclassShop -Dskywalking.collector.backend_service=192.168.10.25:11800
访问 RocketBot 页面 http://192.168.10.25:8080/
批量请求脚本
http://192.168.10.88:8081/api/product/v1/list
http://192.168.10.88:8081/api/product/v1/detail/1
1#!/bin//bash
2##防火墙HTTP访问测试脚本
3echo -e "请输入网址[默认:http://127.0.0.1]"
4read url
5if [[ -z $url ]];then
6 url='http://127.0.0.1'
7fi
8
9echo -e "请输入访问次数[默认:50]"
10read sum
11if [[ -z $sum ]];then
12 sum=50
13else
14 if [[ $sum -lt 1 ]];then
15 echo "访问次数设置错误"
16 exit 1
17 fi
18fi
19echo -e "请输入超时时间[默认:5]"
20read out
21if [[ -z "$out" ]];then
22 out=5
23fi
24if [[ $out -ge 1 || $out -le 50 ]];then
25 echo "超时时间设置符合预期"
26else
27 echo "好像不太符合作者的预期喔"
28 exit 1
29fi
30echo -e "输入数据请求超时时间[默认:10]"
31read data
32if [[ -z $data ]];then
33 data=10
34else
35 if [[ $data -ge 1 || $data -le 30 ]];then
36 echo "数据请求超时时间设置符合预期"
37 else
38 echo "数据请求时间设置不符合预期"
39 exit 1
40 fi
41fi
42echo -e "请输入访问间隔时间[默认:0]"
43read jg
44if [[ -z $jg ]];then
45 jg=0
46else
47 if [[ $jg -ge 0 ]];then
48 echo "设置符合预期"
49 else
50 echo "间隔时间设置不符合预期"
51 exit "1"
52 fi
53fi
54log='./visit.log'
55cmd_file='./cmd.log'
56rm -f $log $cmd_file
57start_time=`date "+%s"`
58for((i=1;i<=$sum;i++));do
59 cmd="curl --connect-timeout $out -m $data $url"
60 echo -e "正在执行[$cmd];执行时间[`date`]" | tee -a $cmd_file
61 $cmd > /dev/null
62 if [[ "$?" -eq "0" ]];then
63 echo -e "第[${i}]次访问[$url] 访问时间[`date`] 访问结果: [成功]" | tee -a $log
64 else
65 echo -e "第[${i}]次访问[${url}] 访问时间[`date`] 访问结果: [失败]" | tee -a $log
66 fi
67 sleep $jg
68done
69end_time=`date "+%s"`
70time=`expr $end_time - $start_time`
71c=`cat $log | grep "成功" | wc -l`
72s=`cat $log | grep "失败" | wc -l`
73echo -e "测试完成,测试结果如下:
74本次访问URL: [$url]
75本次访问总数: [$sum]
76访问成功次数: [$c]
77访问失败次数: [$s]
78访问日志文件: [$cmd_file]
79访问结果日志: [$log]
80测试过程耗时: [$time]秒"
定义 SkyWalking 链路追踪配置实战
-
什么是 TraceId
- 用来标识一条请求链路,一条请求链路中包含一个 Trace ID,多个 Span ID
-
背景
- 对业务代码进行链路追踪,方便排查问题
- 比如,某个接口请求耗时慢,想对业务接口方法进行追踪
- controller->service 方法,记录业务方法加入到链路中,记录入参、返回值等
-
缺点
- 代码有侵入性
添加依赖
1<dependency>
2 <groupId>org.apache.skywalking</groupId>
3 <artifactId>apm-toolkit-trace</artifactId>
4 <version>8.5.0</version>
5 </dependency>
- 业务方法添加注解
POJO 记得重写 toString 方法
1public class ProductServiceImpl implements ProductService {
2
3 @Autowired
4 private ProductMapper productMapper;
5
6 @Override
7 public List<ProductDO> list() {
8 return test();
9 }
10
11 @Trace
12 @Tags(
13 { //arg[1] 代表第二个参数
14 @Tag(key = "test-output", value = "returnedObj")
15 }
16 )
17 public List<ProductDO> test() {
18 try {
19 TimeUnit.SECONDS.sleep(1);
20 } catch (InterruptedException e) {
21 throw new RuntimeException(e);
22 }
23 return productMapper.selectList(new QueryWrapper<>());
24 }
25
26
27 @Override
28 @Trace
29 @Tags(
30 { //arg[1] 代表第二个参数
31 @Tag(key = "findDetailById-input", value = "arg[0]"),
32 @Tag(key = "findDetailById-output", value = "returnedObj")
33 }
34 )
35 public ProductDO findDetailById(long productId) {
36 return productMapper.selectOne(new QueryWrapper<ProductDO>().eq("id",productId));
37 }
38}
入参
返回值
SkyWalking-RocketBot 性能剖析
性能剖析
- 新建任务:新建需要分析的端点
- 左侧列表:对任务进行采样
- 右侧:每个端点的链路信息
- 性能分析:可以查看对应方法的调用栈,找出问题点
- 直接定位到代码方法和代码行
- 注意:一个服务在监控持续时间内只能设置一个端点监控任务
链路追踪-日志和 RPC 上报
添加依赖
1<dependency>
2 <groupId>org.apache.skywalking</groupId>
3 <artifactId>apm-toolkit-logback-1.x</artifactId>
4 <version>8.5.0</version>
5 </dependency>
src/main/resources/logback.xml
1<?xml version="1.0" encoding="UTF-8"?>
2<configuration>
3 <!-- 控制台输出 -->
4 <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
5 <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
6 <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
7 <Pattern>%d{yyyy-MM-dd HH🇲🇲ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</Pattern>
8 </layout>
9 </encoder>
10 </appender>
11
12 <!--系统操作日志-->
13 <root level="INFO">
14 <appender-ref ref="console"/>
15 </root>
16</configuration>
代码打印日志
1import org.slf4j.Logger;
2import org.slf4j.LoggerFactory;
3
4Logger logger = LoggerFactory.getLogger(ProductController.class);
5
6logger.info("分布式链路追踪测试测试!!!");
7
8
9### 控制台输出 TID TraceID
102023-10-09 18:18:33.931 [TID:2166b89c7dd243e199b0f3157301385c.64.16968467138960001] [http-nio-8081-exec-1] INFO n.x.x.controller.ProductController -分布式链路追踪测试测试!!!
将日志上传到 skywalking oap-server 中,查看更方便(日志打印输出到 aop-server)
src/main/resources/logback.xml 中追加 <!-- skywalking grpc 日志收集上报服务端 8.4.0版本后支持 -->
1<?xml version="1.0" encoding="UTF-8"?>
2<configuration>
3 <!-- 控制台输出 -->
4 <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
5 <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
6 <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
7 <Pattern>%d{yyyy-MM-dd HH🇲🇲ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</Pattern>
8 </layout>
9 </encoder>
10 </appender>
11
12 <!-- skywalking grpc 日志收集上报服务端 8.4.0版本后支持 -->
13 <appender name="grpc-log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
14 <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
15 <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
16 <Pattern>%d{yyyy-MM-dd HH🇲🇲ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</Pattern>
17 </layout>
18 </encoder>
19 </appender>
20
21 <!--系统操作日志-->
22 <root level="INFO">
23 <appender-ref ref="console"/>
24 <appender-ref ref="grpc-log"/>
25 </root>
26</configuration>
skywaling agent 默认是上传到本地的 oap,如果 oap server 地址不是本地,则需要单独配置 oapserver 地址
- agent 文件夹里面 config 配置
D:\Project\new\apache-skywalking-apm-bin\agent\config\agent.config
1#日志数据的grpc服务器的主机
2plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:192.168.10.25}
3#日志数据的grpc服务器的端口
4plugin.toolkit.log.grpc.reporter.server_port=${SW_GRPC_LOG_SERVER_PORT:11800}
5#日志数据的最大大小
6plugin.toolkit.log.grpc.reporter.max_message_size=${SW_GRPC_LOG_MAX_MESSAGE_SIZE:10485760}
7#发送数据时将超时多长时间。单位是秒
8plugin.toolkit.log.grpc.reporter.upstream_timeout=${SW_GRPC_LOG_GRPC_UPSTREAM_TIMEOUT:30}
9
10#下面是正式:不加中文注释
11plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:192.168.10.25}
12plugin.toolkit.log.grpc.reporter.server_port=${SW_GRPC_LOG_SERVER_PORT:11800}
13plugin.toolkit.log.grpc.reporter.max_message_size=${SW_GRPC_LOG_MAX_MESSAGE_SIZE:10485760}
14plugin.toolkit.log.grpc.reporter.upstream_timeout=${SW_GRPC_LOG_GRPC_UPSTREAM_TIMEOUT:30}
重启 idea 项目会在 D:\Project\new\apache-skywalking-apm-bin\agent\logs\
目录下生成 skywalking-api.log 文件
- 监控业务相关接口性能,超过阈值则触发告警功能
- 通过调用 webhook 接口进行触发,具体的 webhook 接口地址可以自行定义路径
- 注意:要 OAP Server 的网络可以触发的 webhook 接口
- 开发人员可以在 webhook 接口中编写告警方式,比如邮件、短信等,就是一个 http 接口
链路追踪-告警模块和 WebHook
- 监控业务相关接口性能,超过阈值则触发告警功能
- 通过调用 webhook 接口进行触发,具体的 webhook 接口地址可以自行定义路径
- 注意:要 OAP Server 的网络可以触发的 webhook 接口
- 开发人员可以在 webhook 接口中编写告警方式,比如邮件、短信等,就是一个 http 接口
-
Apache Skywalking 默认的告警规则配置
-
安装目录下的 config 文件夹下 alarm-settings.yml 文件
-
默认内置多个规则
- 最近 3 分钟内服务的平均响应时间超过 1 秒
- 最近 2 分钟服务成功率低于 80%
- 最近 3 分钟 90% 服务响应时间超过 1 秒
- 最近 2 分钟内服务实例的平均响应时间超过 1 秒
-
配置讲解
- metrics-name 脚本中的度量名称
- threshold 阈值
- op 比较操作符,可以设定 >,<,=
- period 多久检查一次当前的指标数据是否符合告警规则,单位分钟
- count 达到多少次后,触发告警消息
- silence-period 在多久时间之内,忽略相同的告警消息,在时间 T 触发了某告警,那么在(T+10)这个时间段,不会再次触发相同告警
- message 告警消息内容
- webhooks 配置告警产生时的触发的调用地址
-
开启 oap-server 的告警 webhook 接口
1、oap-server 规则触发,发送消息到 SpringBoot 项目的自定义 web-hook
2、SpringBoot 项目的自定义 web-hook
记得重启 oap-server 服务读取最新的配置文件 /skywalking/config/alarm-settings.yml
1### 不需要修改容器端口!!!
2# 进入aop-server容器修改配置文件
3# oap-server容器配置文件的路径
4bash-5.0# vi /skywalking/config/alarm-settings.yml
5
6# 1、查看容器映射端口情况
7[root@localhost ~]# docker port xd_oap
811800/tcp -> 0.0.0.0:11800
911800/tcp -> [::]:11800
1012800/tcp -> 0.0.0.0:12800
1112800/tcp -> [::]:12800
12
13# 2、停止运行容器
14[root@localhost ~]# docker stop xd_oap
15
16# 4、查看容器完整的hash_of_the_container数值
17[root@localhost ~]# docker inspect xd_oap | grep Id
18 "Id": "3a9a248620fc6fffa815491b13652188732ab716bedc41f36192cfec04393f35",
19
20# 5、进到/var/lib/docker/containers 目录下找到与全 Id 相同的目录,修改 其中的hostconfig.json 和 config.v2.json文件
21
22[root@localhost ~]# vim /var/lib/docker/containers/3a9a248620fc6fffa815491b13652188732ab716bedc41f36192cfec04393f35/hostconfig.json
23"PortBindings":{"8081/tcp":[{"HostIp":"","HostPort":"8081"}]}
24[root@localhost ~]# vim /var/lib/docker/containers/3a9a248620fc6fffa815491b13652188732ab716bedc41f36192cfec04393f35/config.v2.json
25"ExposedPorts":{"8081/tcp":{},"80/tcp":{}}
26
27# 6、重启docker
28[root@localhost ~]# systemctl restart docker
29
30# 7、启动容器
31[root@localhost ~]# docker start xd_oap
32xd_oap
33[root@localhost ~]# docker port xd_oap
348081/tcp -> 0.0.0.0:8081
358081/tcp -> [::]:8081
3611800/tcp -> 0.0.0.0:11800
3711800/tcp -> [::]:11800
3812800/tcp -> 0.0.0.0:12800
3912800/tcp -> [::]:12800
40
41# 8、修改oap-server容器配置文件的路径
42docker exec -it xd_oap /bin/bash
43bash-5.0# vi /skywalking/config/alarm-settings.yml
44#webhooks:
45# - http://127.0.0.1/notify/
46# - http://127.0.0.1/go-wechat/
47 - http://127.0.0.1:8081/webhook/
48
49# 9、重启容器
50[root@localhost ~]# docker restart xd_oap
src/main/java/net/xdclass/xdclasssky/model/AlarmMessage.java
1package net.xdclass.xdclasssky.model;
2
3import lombok.Data;
4
5@Data
6public class AlarmMessage {
7 private int scopeId;
8 private String scope;
9 private String name;
10 private String id0;
11 private String id1;
12 private String ruleName;
13 private String alarmMessage;
14 private long startTime;
15 private transient int period;
16 private transient boolean onlyAsCondition;
17
18}
src/main/java/net/xdclass/xdclasssky/controller/CallbackController.java
1package net.xdclass.xdclasssky.controller;
2
3import net.xdclass.xdclasssky.model.AlarmMessage;
4import org.springframework.web.bind.annotation.GetMapping;
5import org.springframework.web.bind.annotation.PostMapping;
6import org.springframework.web.bind.annotation.RequestBody;
7import org.springframework.web.bind.annotation.RestController;
8
9import java.util.ArrayList;
10import java.util.List;
11@RestController
12public class CallbackController {
13 private List<AlarmMessage> messageList = new ArrayList<>();
14
15 /**
16 * 要跟 oap-server 暴露的接口保持一致 (Spingboot项目自定义的webhook地址)
17 * - http://192.168.10.88:8081/webhook/
18 * @param alarmMessageList
19 */
20 @PostMapping("/webhook")
21 public void webhook(@RequestBody List<AlarmMessage> alarmMessageList){
22 System.out.println("收到消息:" +alarmMessageList);
23 messageList = alarmMessageList;
24 }
25
26 /**
27 * 临时接口方便查看
28 * @return
29 */
30 @GetMapping("/show")
31 public List<AlarmMessage> show(){
32 return messageList;
33 }
34}
测试请求路径
- 修改睡眠时间为 2 秒,模拟接口响应慢
控制台收到消息
1收到消息:[AlarmMessage(scopeId=2, scope=SERVICE_INSTANCE, name=927a70eb91d745aab7d9bfa28657ef4b@192.168.10.88 of XdclassShop, id0=WGRjbGFzc1Nob3A=.1_OTI3YTcwZWI5MWQ3NDVhYWI3ZDliZmEyODY1N2VmNGJAMTkyLjE2OC4xMC44OA==, id1=, ruleName=service_instance_resp_time_rule, alarmMessage=Response time of service instance 927a70eb91d745aab7d9bfa28657ef4b@192.168.10.88 of XdclassShop is more than 1000ms in 2 minutes of last 10 minutes, startTime=1696859365690, period=0, onlyAsCondition=false)]
整合钉钉报警
application.properties
1server.port=8081
2spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver
3spring.datasource.url=jdbc:mysql://192.168.10.25:3306/xdclass_sky_shop?useUnicode=true&characterEncoding=utf-8&useSSL=false&&allowPublicKeyRetrieval=true
4spring.datasource.username =root
5spring.datasource.password =123456
6dingtalk.robot.webhook = https://oapi.dingtalk.com/robot/send?access_token=ed1d4c0432899cdc43fd874c117869695f1bed7fa5e74b37efcc4d763ae1b623
src/main/java/net/xdclass/xdclasssky/service/DingTalkRobotService.java
1package net.xdclass.xdclasssky.service;
2
3import org.springframework.beans.factory.annotation.Value;
4import org.springframework.http.HttpEntity;
5import org.springframework.http.HttpHeaders;
6import org.springframework.http.MediaType;
7import org.springframework.stereotype.Service;
8import org.springframework.web.client.RestTemplate;
9
10@Service
11public class DingTalkRobotService {
12
13 @Value("${dingtalk.robot.webhook}")
14 private String webhook;
15
16 private RestTemplate restTemplate = new RestTemplate();
17
18 public void sendMessage(String message) {
19 HttpHeaders headers = new HttpHeaders();
20 headers.setContentType(MediaType.APPLICATION_JSON);
21
22 String payload = "{\"msgtype\":\"text\",\"text\":{\"content\":\"" + message + "\"}}";
23 HttpEntity<String> entity = new HttpEntity<>(payload, headers);
24
25 restTemplate.postForEntity(webhook, entity, String.class);
26 }
27}
src/main/java/net/xdclass/xdclasssky/controller/CallbackController.java
1package net.xdclass.xdclasssky.controller;
2
3import net.xdclass.xdclasssky.model.AlarmMessage;
4import net.xdclass.xdclasssky.service.DingTalkRobotService;
5import org.springframework.beans.factory.annotation.Autowired;
6import org.springframework.web.bind.annotation.GetMapping;
7import org.springframework.web.bind.annotation.PostMapping;
8import org.springframework.web.bind.annotation.RequestBody;
9import org.springframework.web.bind.annotation.RestController;
10
11import java.util.ArrayList;
12import java.util.List;
13@RestController
14public class CallbackController {
15 private List<AlarmMessage> messageList = new ArrayList<>();
16
17 @Autowired
18 private DingTalkRobotService dingTalkRobotService;
19
20 /**
21 * 要跟 oap-server 暴露的接口保持一致 (Spingboot项目自定义的webhook地址)
22 * - http://192.168.10.88:8081/webhook/
23 * @param alarmMessageList
24 */
25 @PostMapping("/webhook")
26 public void webhook(@RequestBody List<AlarmMessage> alarmMessageList){
27 System.out.println("收到消息:" +alarmMessageList);
28 dingTalkRobotService.sendMessage(alarmMessageList.get(0).getAlarmMessage());
29 messageList = alarmMessageList;
30 }
31
32 /**
33 * 临时接口方便查看
34 * @return
35 */
36 @GetMapping("/show")
37 public List<AlarmMessage> show(){
38 return messageList;
39 }
40}
jar 方式打包运行整合 Skywalking
-
项目打包
-
SpringBoot 项目和 SpringCloud 项目都一样,jar 方式运行
-
通过 Java -jar 加入参数
-
项目打包
1mvn install
-
-
例子
1java -jar -javaagent:/Users/xdclass/Desktop/agent/skywalking-agent.jar -Dskywalking.agent.service_name=XdclassShop -Dskywalking.collector.backend_service=127.0.0.1:11800 xdclass-sky-0.0.1-SNAPSHOT.jar