目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

Apache SkyWalking

布式架构下的排查问题复杂性

分布式应用架构的运维和诊断的过程变得越来越复杂,例如:接口诊断困难、应用性能诊断复杂、架构分析复杂等难题,传统的监控工具并无法满足,分布式链路系统由此诞生

核心:将一次请求分布式调用,使用GPS定位串起来,记录每个调用的耗时、性能等日志,并通过可视化工具展示出来

APM系统(Application Performance Management)

  • 讲到分布式的链路追踪系统,不得不说说它的前世今生了,APM系统是可以帮助系统的行为做性能分析的工具
  • APM系统,它是由谷歌公开的论文提到的,可以说谷歌还是很强的!而到后面,许多的技术公司就基于这边论文的原理,开发出来很多出色的APM框架,比如:skywalking、zipkin等等

SkyWalking

  • Skywalking是什么

  • 市场上同类解决方案

    • Zipkin是由Twitter开源的链路分析分析工具,在springcloud sleuth得到了广泛的使用,具有轻量,部署简单的特点
    • Pinpoint是由韩国人开发的链路追踪应用监控分析工具,基于字节码方式注入。具有支持多种插件,UI功能强大,接入端没有代码侵入
    • Skywalking是由国人开发的链路追踪应用监控分析工具,基于字节码方式注入。具有支持多种插件,UI功能强大,接入端没有代码侵入,现已加入Apache孵化器
    • CAT是大众点评开源的链路追踪分析工具,具有对应用监控的分析、日志的采集、监控报警一系列的监控平台
    项目CatZipkinSkywalking
    链路追踪可视化
    聚合报表良好中等
    服务的依赖图良好良好
    埋点方式入侵式入侵式非入侵式,字节码增强
    VM监控指标没有
    存储机制mysql、本地文件内存、es、mysql、等H2、es、mysql、TIDB等
    支持语言java/.net丰富丰富
    社区支持国内国外主流apache支持
    知名使用者美团、携程京东、阿里定制不开源华为、小米
    APM
    开发基础eBay calGoogle DapperGoogle Dapper
    支持webflux不支持支持支持
    github下载量12.3k12.2k19.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.先安装yml
yum install -y yum-utils device-mapper-persistent-data lvm2

# 2.设置阿里云镜像
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 3.查看可安装的docker版本
yum list docker-ce --showduplicates | sort -r

#4. 安装docker
yum -y install docker-ce-20.10.10-3.el7

#5. 查看docker版本
docker -v

#6. 启动docker
systemctl start docker

#7. 查看docker 启动状态
systemctl status docker

#查看端口占用命令安装
yum 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

# 创建配置文件目录、数据持久化目录
[root@localhost ~]# mkdir -p /mydata/es/config
[root@localhost ~]# mkdir -p /mydata/es/data
[root@localhost ~]# chmod 777 -R /mydata/es

# 创建配置文件并开启远程连接
[root@localhost ~]# echo "http.host: 0.0.0.0" >> /mydata/es/config/elasticsearch.yml

# 启动运行
docker run -d --name xdclass_es7 -p 9200:9200 -p 9300:9300 \
  -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
  -v /mydata/es/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
  -v /mydata/es/data:/usr/share/elasticsearch/data \
  -v /mydata/es/plugins:/usr/share/elasticsearch/plugins elasticsearch:7.6.2

参数说明
-e "discovery.type=single-node" 设置为单节点
-e ES_JAVA_OPTS="-Xms512m -Xmx512m" 设置ES的初始内存和最大内存,否则过大启动不了ES

注意:这时候会提示权限不够需要开启权限:chmod 777 -R /mydata/es
     登录http://ip:9200/_cat/nodes?v=true&pretty
     记得开放网络安全组 9200 9300

# 访问验证
http://192.168.10.25:9200/_cat/nodes?v=true&pretty

Skywalking部署:数据存储OAP-Server

docker 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

参数:
--link <name or id>:alias ,添加到另一个容器的链接,可以添加别名或者不加
–link后面的参数和elasticsearch容器名一致;
SW_STORAGE=elasticsearch7 是固定的,使用es7;
SW_STORAGE_ES_CLUSTER_NODES:xdclass_es7也可改为es服务器部署的Ip地址,比如ip:9200

Skywalking部署:数据存储UI

# 安装ui
docker run -d --name xdclass_skywalking-ui \
--restart=always \
-e TZ=Asia/Shanghai \
-p 8080:8080 \
--link xd_oap \
-e SW_OAP_ADDRESS=xd_oap:12800 \
apache/skywalking-ui:8.5.0

# SkyWalking UI界面访问地址
http://192.168.10.25:8080/

# 查看ElasticSearch全部索引
http://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越多代表全年服务可用时间越长服务更可靠,停机时间越短
1年 = 365天 = 8760小时

99.9 = 8760 * 0.1% = 8760 * 0.001 = 8.76小时

99.99 = 8760 * 0.0001 = 0.876小时 = 0.876 * 60 = 52.6分钟

99.999 = 8760 * 0.00001 = 0.0876小时 = 0.0876 * 60 = 5.26分钟

从以上看来,全年停机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

###  数据库名字:xdclass_sky_shop
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for product
-- ----------------------------
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
  `id` bigint NOT NULL,
  `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '商品标题',
  `detail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '详情',
  `img` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '图片',
  `amount` decimal(16,0) DEFAULT NULL COMMENT '现价',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

-- ----------------------------
-- Records of product
-- ----------------------------
BEGIN;
INSERT INTO `product` VALUES (1, '商用短链平台-海量数据项目大课', '打造最佳简历项目的,短链平台大课项目', 'https://file.xdclass.net/video/2022/71-HLSJCL/05.jpeg', 66);
INSERT INTO `product` VALUES (2, '新一代AlibabaCloud全家桶实战', '高并发分布式项目', 'https://file.xdclass.net/video/2022/71-HLSJCL/05.jpeg', 88);
INSERT INTO `product` VALUES (3, '小滴课堂永久会员', '观看全部IT专题视频,永久有效', 'https://file.xdclass.net/video/2022/banner/03.jpeg', 999);
INSERT INTO `product` VALUES (4, '架构大课训练营', '全方位提升综合能力,存储、网络、架构、解决方案', 'https://file.xdclass.net/video/2022/72-ShardingJDBC/banner.jpeg', 79);
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

引入依赖

<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.26</version>
		</dependency>

		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.4.1</version>
		</dependency>

application.properties

server.port=8081
#==============================???????========================================
spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.10.25:3306/xdclass_sky_shop?useUnicode=true&characterEncoding=utf-8&useSSL=false&&allowPublicKeyRetrieval=true
spring.datasource.username =root
spring.datasource.password =123456

开发接口

  • 商品列表接口
  • 商品详情接口

model

package net.xdclass.xdclasssky.model;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.math.BigDecimal;

@TableName("product")
@Data
public class ProductDO {

    private Long id;

    /**
     * 商品标题
     */
    private String title;

    /**
     * 详情
     */
    private String detail;

    /**
     * 图片
     */
    private String img;

    /**
     * 现价
     */
    private BigDecimal amount;

    // set、get方法
}

model:工具类jsondata

package net.xdclass.xdclasssky.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class JsonData {
    private int code;
    private Object object;
    private String msg;

    public static JsonData buildSuccess(Object data) {
        return new JsonData(0, data, "");
    }
}

mapper

package net.xdclass.xdclasssky.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.xdclass.xdclasssky.model.ProductDO;

public interface ProductMapper extends BaseMapper<ProductDO> {
}

service:interface

package net.xdclass.xdclasssky.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.xdclass.xdclasssky.model.ProductDO;

public interface ProductMapper extends BaseMapper<ProductDO> {
}

service:Impl

package net.xdclass.xdclasssky.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import net.xdclass.xdclasssky.mapper.ProductMapper;
import net.xdclass.xdclasssky.model.ProductDO;
import net.xdclass.xdclasssky.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductMapper productMapper;

    @Override
    public List<ProductDO> list() {
        return productMapper.selectList(new QueryWrapper<>());
    }

    @Override
    public ProductDO findDetailById(long productId) {
        return productMapper.selectOne(new QueryWrapper<ProductDO>().eq("id",productId));
    }


}

controller

package net.xdclass.xdclasssky.controller;

import net.xdclass.xdclasssky.model.JsonData;
import net.xdclass.xdclasssky.model.ProductDO;
import net.xdclass.xdclasssky.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/api/product/v1")
public class ProductController {

    @Autowired
    private ProductService productService;

    /**
     * 查看商品列表接口
     *
     * @return
     */
    @GetMapping("list")
    public JsonData list() {

        List<ProductDO> list = productService.list();
        return JsonData.buildSuccess(list);
    }

    /**
     * 查看商品详情
     *
     * @param productId
     * @return
     */
    @GetMapping("/detail/{product_id}")
    public JsonData detail(@PathVariable("product_id") long productId) {
        ProductDO productDO = productService.findDetailById(productId);
        return JsonData.buildSuccess(productDO);
    }
}

XdclassSkyApplication

package net.xdclass.xdclasssky;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("net.xdclass.xdclasssky.mapper")
public class XdclassSkyApplication {

	public static void main(String[] args) {
		SpringApplication.run(XdclassSkyApplication.class, args);
	}

}

测试接口API

http://192.168.10.88:8081/api/product/v1/list
http://192.168.10.88:8081/api/product/v1/detail/1

Skywalking Agent(探针)

  • 什么SkyWalking探针

    • 探针表示集成到目标系统中的代理或 SDK 库,即收集并格式化数据, 并发送到后端 包括链路追踪和性能指标
  • Skywalking-Agent的安装和使用

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}
-javaagent:../skywalking-agent.jar=agent.service_name=XdclassApp,collector.backend_service=127.0.0.1:11800
  • 第二种:-Dskywalking.[option1]=[value2]
-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的地址上传的服务地址

    
    

路径中不要出现中文目录

-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

#!/bin//bash                                                                                                                                                              
##防火墙HTTP访问测试脚本                                                                                                                                                  
echo -e "请输入网址[默认:http://127.0.0.1]"                                                                                                                               
read url                                                                                                                                                                  
if [[ -z $url ]];then                                                                                                                                                     
        url='http://127.0.0.1'                                                                                                                                            
fi                                                                                                                                                                        
                                                                                                                                                                          
echo -e "请输入访问次数[默认:50]"                                                                                                                                         
read sum                                                                                                                                                                  
if [[ -z $sum ]];then
        sum=50
else
        if [[ $sum -lt 1 ]];then
                echo "访问次数设置错误"
                exit 1
        fi
fi
echo -e "请输入超时时间[默认:5]"
read out
if [[ -z "$out" ]];then
        out=5
fi
if [[ $out -ge 1 || $out -le 50 ]];then
        echo "超时时间设置符合预期"
else
         echo "好像不太符合作者的预期喔"
         exit 1
fi
echo -e "输入数据请求超时时间[默认:10]"
read data
if [[ -z  $data ]];then
        data=10
else
        if [[ $data -ge 1 || $data -le 30 ]];then
                echo "数据请求超时时间设置符合预期"
        else
                echo "数据请求时间设置不符合预期"
                exit 1
        fi
fi
echo -e "请输入访问间隔时间[默认:0]"
read jg
if [[ -z $jg ]];then
        jg=0
else
        if [[ $jg -ge 0 ]];then
                echo "设置符合预期"
        else
                echo "间隔时间设置不符合预期"
                exit "1"
        fi
fi
log='./visit.log'
cmd_file='./cmd.log'
rm -f $log $cmd_file 
start_time=`date "+%s"`
for((i=1;i<=$sum;i++));do
        cmd="curl --connect-timeout $out -m $data  $url"
        echo -e "正在执行[$cmd];执行时间[`date`]" | tee -a $cmd_file
        $cmd > /dev/null
        if [[ "$?" -eq "0" ]];then
                echo -e "第[${i}]次访问[$url]  访问时间[`date`]  访问结果: [成功]" | tee -a $log
        else
                echo -e "第[${i}]次访问[${url}] 访问时间[`date`] 访问结果: [失败]" | tee -a $log
        fi
        sleep $jg
done
end_time=`date "+%s"`
time=`expr $end_time - $start_time`
c=`cat $log | grep "成功" | wc -l`
s=`cat $log | grep "失败" | wc -l`
echo -e "测试完成,测试结果如下:
本次访问URL: [$url]
本次访问总数: [$sum]
访问成功次数: [$c]
访问失败次数: [$s]
访问日志文件: [$cmd_file]
访问结果日志: [$log]
测试过程耗时: [$time]秒"

定义SkyWalking链路追踪配置实战

  • 什么是TraceId

    • 用来标识一条请求链路,一条请求链路中包含一个Trace ID,多个Span ID
  • 背景

    • 对业务代码进行链路追踪,方便排查问题
    • 比如,某个接口请求耗时慢,想对业务接口方法进行追踪
    • controller->service 方法,记录业务方法加入到链路中,记录入参、返回值等
  • 缺点

    • 代码有侵入性

添加依赖

<dependency>
			<groupId>org.apache.skywalking</groupId>
			<artifactId>apm-toolkit-trace</artifactId>
			<version>8.5.0</version>
		</dependency>
  • 业务方法添加注解
    POJO记得重写toString方法
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductMapper productMapper;

    @Override
    public List<ProductDO> list() {
        return test();
    }

    @Trace
    @Tags(
            {       //arg[1] 代表第二个参数
                    @Tag(key = "test-output", value = "returnedObj")
            }
    )
    public List<ProductDO> test() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return productMapper.selectList(new QueryWrapper<>());
    }


    @Override
    @Trace
    @Tags(
            {       //arg[1] 代表第二个参数
                    @Tag(key = "findDetailById-input", value = "arg[0]"),
                    @Tag(key = "findDetailById-output", value = "returnedObj")
            }
    )
    public ProductDO findDetailById(long productId) {
        return productMapper.selectOne(new QueryWrapper<ProductDO>().eq("id",productId));
    }
}

入参

返回值

SkyWalking-RocketBot性能剖析

性能剖析

  • 新建任务:新建需要分析的端点
  • 左侧列表:对任务进行采样
  • 右侧:每个端点的链路信息
  • 性能分析:可以查看对应方法的调用栈,找出问题点
    • 直接定位到代码方法和代码行
  • 注意:一个服务在监控持续时间内只能设置一个端点监控任务

链路追踪-日志和RPC上报

添加依赖

<dependency>
			<groupId>org.apache.skywalking</groupId>
			<artifactId>apm-toolkit-logback-1.x</artifactId>
			<version>8.5.0</version>
		</dependency>

src/main/resources/logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>  
    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
                <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</Pattern>
            </layout>
        </encoder>
    </appender>

    <!--系统操作日志-->
    <root level="INFO">  
        <appender-ref ref="console"/> 
    </root>
</configuration>

代码打印日志

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Logger logger = LoggerFactory.getLogger(ProductController.class);

logger.info("分布式链路追踪测试测试!!!");


### 控制台输出  TID TraceID
2023-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版本后支持 -->

<?xml version="1.0" encoding="UTF-8"?>
<configuration>  
    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
                <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</Pattern>
            </layout>
        </encoder>
    </appender>

    <!-- skywalking grpc 日志收集上报服务端 8.4.0版本后支持 -->
    <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{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</Pattern>
            </layout>
        </encoder>
    </appender>

    <!--系统操作日志-->
    <root level="INFO">  
        <appender-ref ref="console"/> 
        <appender-ref ref="grpc-log"/> 
    </root>
</configuration>

skywaling agent 默认是上传到本地的oap,如果oap server地址不是本地,则需要单独配置oapserver地址

  • agent文件夹里面config配置

D:\Project\new\apache-skywalking-apm-bin\agent\config\agent.config

#日志数据的grpc服务器的主机
plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:192.168.10.25} 
#日志数据的grpc服务器的端口
plugin.toolkit.log.grpc.reporter.server_port=${SW_GRPC_LOG_SERVER_PORT:11800}
#日志数据的最大大小
plugin.toolkit.log.grpc.reporter.max_message_size=${SW_GRPC_LOG_MAX_MESSAGE_SIZE:10485760}
#发送数据时将超时多长时间。单位是秒
plugin.toolkit.log.grpc.reporter.upstream_timeout=${SW_GRPC_LOG_GRPC_UPSTREAM_TIMEOUT:30}

#下面是正式:不加中文注释
plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:192.168.10.25}
plugin.toolkit.log.grpc.reporter.server_port=${SW_GRPC_LOG_SERVER_PORT:11800}
plugin.toolkit.log.grpc.reporter.max_message_size=${SW_GRPC_LOG_MAX_MESSAGE_SIZE:10485760}
plugin.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

### 不需要修改容器端口!!!
# 进入aop-server容器修改配置文件
# oap-server容器配置文件的路径
bash-5.0# vi /skywalking/config/alarm-settings.yml

# 1、查看容器映射端口情况
[root@localhost ~]# docker port xd_oap
11800/tcp -> 0.0.0.0:11800
11800/tcp -> [::]:11800
12800/tcp -> 0.0.0.0:12800
12800/tcp -> [::]:12800

# 2、停止运行容器
[root@localhost ~]# docker stop xd_oap

# 4、查看容器完整的hash_of_the_container数值
[root@localhost ~]# docker inspect xd_oap | grep Id
        "Id": "3a9a248620fc6fffa815491b13652188732ab716bedc41f36192cfec04393f35",

# 5、进到/var/lib/docker/containers 目录下找到与全 Id 相同的目录,修改 其中的hostconfig.json 和 config.v2.json文件

[root@localhost ~]# vim /var/lib/docker/containers/3a9a248620fc6fffa815491b13652188732ab716bedc41f36192cfec04393f35/hostconfig.json
"PortBindings":{"8081/tcp":[{"HostIp":"","HostPort":"8081"}]}
[root@localhost ~]# vim /var/lib/docker/containers/3a9a248620fc6fffa815491b13652188732ab716bedc41f36192cfec04393f35/config.v2.json
"ExposedPorts":{"8081/tcp":{},"80/tcp":{}}

# 6、重启docker
[root@localhost ~]# systemctl restart docker

# 7、启动容器
[root@localhost ~]# docker start xd_oap
xd_oap
[root@localhost ~]# docker port xd_oap
8081/tcp -> 0.0.0.0:8081
8081/tcp -> [::]:8081
11800/tcp -> 0.0.0.0:11800
11800/tcp -> [::]:11800
12800/tcp -> 0.0.0.0:12800
12800/tcp -> [::]:12800

# 8、修改oap-server容器配置文件的路径
docker exec -it xd_oap /bin/bash
bash-5.0# vi /skywalking/config/alarm-settings.yml
#webhooks:
#  - http://127.0.0.1/notify/
#  - http://127.0.0.1/go-wechat/
   - http://127.0.0.1:8081/webhook/

# 9、重启容器
[root@localhost ~]# docker restart xd_oap

src/main/java/net/xdclass/xdclasssky/model/AlarmMessage.java

package net.xdclass.xdclasssky.model;

import lombok.Data;

@Data
public class AlarmMessage {
    private int scopeId;
    private String scope;
    private String name;
    private String id0;
    private String id1;
    private String ruleName;
    private String alarmMessage;
    private long startTime;
    private transient int period;
    private transient boolean onlyAsCondition;

}

src/main/java/net/xdclass/xdclasssky/controller/CallbackController.java

package net.xdclass.xdclasssky.controller;

import net.xdclass.xdclasssky.model.AlarmMessage;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
@RestController
public class CallbackController {
    private List<AlarmMessage> messageList = new ArrayList<>();

    /**
     * 要跟 oap-server 暴露的接口保持一致  (Spingboot项目自定义的webhook地址)
     *  - http://192.168.10.88:8081/webhook/
     * @param alarmMessageList
     */
    @PostMapping("/webhook")
    public void  webhook(@RequestBody List<AlarmMessage> alarmMessageList){
        System.out.println("收到消息:" +alarmMessageList);
        messageList = alarmMessageList;
    }

    /**
     * 临时接口方便查看
     * @return
     */
    @GetMapping("/show")
    public List<AlarmMessage> show(){
        return messageList;
    }
}

测试请求路径

  • 修改睡眠时间为2秒,模拟接口响应慢

控制台收到消息

收到消息:[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

server.port=8081
spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.10.25:3306/xdclass_sky_shop?useUnicode=true&characterEncoding=utf-8&useSSL=false&&allowPublicKeyRetrieval=true
spring.datasource.username =root
spring.datasource.password =123456
dingtalk.robot.webhook = https://oapi.dingtalk.com/robot/send?access_token=ed1d4c0432899cdc43fd874c117869695f1bed7fa5e74b37efcc4d763ae1b623

src/main/java/net/xdclass/xdclasssky/service/DingTalkRobotService.java

package net.xdclass.xdclasssky.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class DingTalkRobotService {

    @Value("${dingtalk.robot.webhook}")
    private String webhook;

    private RestTemplate restTemplate = new RestTemplate();

    public void sendMessage(String message) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        String payload = "{\"msgtype\":\"text\",\"text\":{\"content\":\"" + message + "\"}}";
        HttpEntity<String> entity = new HttpEntity<>(payload, headers);

        restTemplate.postForEntity(webhook, entity, String.class);
    }
}

src/main/java/net/xdclass/xdclasssky/controller/CallbackController.java

package net.xdclass.xdclasssky.controller;

import net.xdclass.xdclasssky.model.AlarmMessage;
import net.xdclass.xdclasssky.service.DingTalkRobotService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
@RestController
public class CallbackController {
    private List<AlarmMessage> messageList = new ArrayList<>();

    @Autowired
    private DingTalkRobotService dingTalkRobotService;

    /**
     * 要跟 oap-server 暴露的接口保持一致   (Spingboot项目自定义的webhook地址)
     *  - http://192.168.10.88:8081/webhook/
     * @param alarmMessageList
     */
    @PostMapping("/webhook")
    public void  webhook(@RequestBody List<AlarmMessage> alarmMessageList){
        System.out.println("收到消息:" +alarmMessageList);
        dingTalkRobotService.sendMessage(alarmMessageList.get(0).getAlarmMessage());
        messageList = alarmMessageList;
    }

    /**
     * 临时接口方便查看
     * @return
     */
    @GetMapping("/show")
    public List<AlarmMessage> show(){
        return messageList;
    }
}

jar方式打包运行整合Skywalking

  • 项目打包

    • springboot项目和springcloud项目都一样,jar方式运行

    • 通过java -jar 加入参数

    • 项目打包

      mvn install
      
  • 例子

    java -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
    

作者:Soulboy