springCloud二-SkyWalking3-性能剖析-⽇志上传-告警管理-接入飞书

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • [1. 性能剖析](#1. 性能剖析)
  • [2. ⽇志上传](#2. ⽇志上传)
    • [2.1 ⽇志配置](#2.1 ⽇志配置)
  • [3. 告警管理](#3. 告警管理)
    • [3.1 告警规则](#3.1 告警规则)
    • [3.2 Webhook](#3.2 Webhook)
      • [3.2.1 SkyWalking 的 WebHook 实现](#3.2.1 SkyWalking 的 WebHook 实现)
        • [3.2.1.1 告警触发与推送](#3.2.1.1 告警触发与推送)
        • [3.2.1.2 WebHook配置](#3.2.1.2 WebHook配置)
        • [3.2.1.3 典型应⽤场景](#3.2.1.3 典型应⽤场景)
      • [3.2.2 WebHook实践](#3.2.2 WebHook实践)
        • [3.2.2.1 配置WebHook](#3.2.2.1 配置WebHook)
        • [3.2.2.2 接⼝开发](#3.2.2.2 接⼝开发)
      • [3.2.3 配置126网易邮件告警](#3.2.3 配置126网易邮件告警)
  • [3.3 接入飞书](#3.3 接入飞书)
  • 总结

前言

1. 性能剖析

性能剖析功能是⼀种针对分布式系统代码级性能的动态分析技术, SkyWalking 提供了追踪分析的功能,可以查看请求调⽤链中具体⽅法或者代码块的执⾏耗时, ⽤于快速识别⾼耗时⽅法(如慢SQL查询等), 定位服务调⽤链(Trace)中具体 Span(单个操作节点)的性能问题

就是分析每一行代码的消耗时间

点击trace profing

点击新建任务

选择query9

点击新建任务

发现第一行耗时最长

点击这一行,然后点击分析


我们找到这里,说明是这个执行了2000ms

还知道是35行代码耗时

com.bite.order.controller.OrderController.queryOrder:35

果然是35行

这样我们就知道是哪里代码慢了

2. ⽇志上传

SkyWalking不仅⽀持链路追踪, 还可以集成⽇志数据, 帮助⽤⼾在⼀个平台上统⼀查看⽇志和追踪信息, 基于 Trace ID 实现⽇志与请求链路的⾃动关联,快速定位故障上下⽂.

java 复制代码
    public OrderInfo queryOrder(Integer orderId) {
        log.info("queryOrder,orderId:{}",orderId);
        return orderMapper.selectById(orderId);
    }

问题是什么呢,问题就是如果是多线程的话,打印日志的顺序就可能不是按照一个线程打印一堆了,而是按照一种日志打印一堆---》比较混乱

我们只需要在trace这里点击查看日志

就是把控制台的日志上传到SkyWalking中

配置参考

java 复制代码
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-logback-1.x</artifactId>
            <version>9.4.0</version>
        </dependency>

2.1 ⽇志配置

我们现在的id与traceId无关,没有关联起来

但是SkyWalking是有traceId的

所以要配置TraceId,还有上报到SkyWalking中

添加 logback-spring.xml ⽂件

• Logback 框架默认加载的配置⽂件名为 logback.xml , 该名称适⽤于⾮ Spring Boot 应⽤.

• Spring Boot默认会识别logback-spring.xml, 这是Spring Boot推荐的⽅式, 优先级⾼于默认logback.xml, ⽀持 Spring 扩展特性(如 ${spring.profiles.active})

• 若同时存在 logback.xml 和 logback-spring.xml , Spring Boot 优先加载 logback-spring.xml

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">

    
    
</configuration>

大框架

在日志中打印traceId

set %tid in Pattern section of logback.xml

java 复制代码
    <appender name="STDOUT" 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>

appender 是负责日志的输出的核心组件,把日志内容输出到指定的目标

ConsoleAppender表示输出到控制台

Pattern表示输出的格式

其中%tid就是traceId

java 复制代码
        <root level="INFO">
            <appender-ref ref="STDOUT"/>
        </root>

这个是定义STDOUT日志输出的级别

这个TID就是traceID

然后是日志上报到SkyWalking中

grpc上报日志到SkyWalking中

java 复制代码
    <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} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n</Pattern>
            </layout>
        </encoder>
    </appender>

GRPCLogClientAppender就是上报日志到SkyWalking

java 复制代码
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="grpc-log"/>
    </root>

这样就可以了

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">

    <appender name="STDOUT" 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>

    <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} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n</Pattern>
            </layout>
        </encoder>
    </appender>


    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="grpc-log"/>
    </root>

</configuration>

这是总体代码

这样日志就来了

而且它把每一个trace的日志都是放在一起的

这些上传的日志都是由于trace引起的日志,有traceId才行

启动的时候的日志就不会上传了

3. 告警管理

除了指标的监测之外, SkyWalking 还提供了告警功能. 当系统出现异常时(如接⼝响应慢, 成功率低), ⾃动触发通知, 帮助研发⼈员或者运维⼈员快速定位问题

点击告警

点击进入有告警详情

希望配置告警,比如成功率低于80%的时候告警
还有就是告警希望可以通过邮件等形式告诉我,不然我不可能一直盯着页面看吧

3.1 告警规则

SkyWalking 的告警系统预先定义了⼀部分告警规则, 在 config/alarm-settings.yml ⽂件中, ⽤⼾也可以⾃定义告警规则.

告警规则

默认告警规则

为⽅便起⻅,SkyWalking提供了⼀个默认的alarm-setting.yml, 包括以下规则:

  1. 过去 3 分钟内服务平均响应时间超过 1 秒。
  2. 最近 2 分钟服务成功率低于 80%。
  3. 过去 3 分钟内超过 1 秒的服务响应时间百分位数
  4. 服务实例在过去 2 分钟内的平均响应时间超过 1 秒,并且实例名称与正则表达式匹配。
  5. 终端节点在过去 2 分钟内的平均响应时间超过 1 秒。
  6. 过去 2 分钟内数据库访问平均响应时间超过 1 秒。
  7. 终端节点关系 过去 2 分钟内超过 1 秒的平均响应时间。

service_resp_time_rule是告警的规则名称

规则名称必须是唯一的,而且必须以_rule结尾

expression是告警规则的配置

sum(service_resp_time > 1000) >= 3的意思就是一分钟内(一分钟统计)有三次响应时间大于一秒钟------》告警

period:10表示统计最近十分钟内的

java 复制代码
    silence-period: 5

这个表示静默时长,报警之后5分钟之内,就不报警了

message表示告警的信息

java 复制代码
XXXX_rule: 规则名称, 必须以"_rule" 结尾
• expression:告警表达式, 结果为1时, 触发报警
• period: 告警周期, 评估指标的时间⻓度(以分钟为单位)
• silence-period: 静默期, 在告警触发后, 多⻓时间内不再触发.
	在Time-N (TN)触发告警后,在"TN -> TN +period"时间段内保持静默.
	默认情况下, 它的⼯作⽅式与period相同. 同⼀告警在⼀个周期内只能触发⼀次
• message: 告警消息

所以service_resp_time_rule就是过去 3 分钟内服务平均响应时间超过 1 秒。

database_access_resp_time_rule:过去 2 分钟内数据库访问平均响应时间超过 1 秒。

endpoint_relation_resp_time_rule:终端节点关系 过去 2 分钟内超过 1 秒的平均响应时间。等等

rules:下面的_rule都是SkyWalking默认支持的告警---》都是会生效的

当然你也可以在alarm-settings.yml这个文件中自己配置规则

java 复制代码
#hooks:
#  webhook:
#    default:
#      is-default: true
#      urls:
#        - http://127.0.0.1/notify/
#        - http://127.0.0.1/go-wechat/

这个配置呢

默认告警会显示在SkyWalking的UI界面上

webhook就是主动通知我们告警信息的,而不是让我们自己去ui界面查看

3.2 Webhook

webhook是⼀种允许应⽤程序向外部系统(飞书,微信)实时推送事件或数据的机制, 通常通过HTTP回调实现, 从⽽实现跨系统⾃动化的信息传递

核⼼特征:

• 事件驱动: 当预设条件触发时(如告警触发、数据更新), 主动向⽬标 URL 发送 HTTP 请求(通常为POST)

• 轻量级集成: 接收⽅只需提供⼀个可访问的 HTTP 端点即可接收数据, ⽆需轮询查询

• 灵活扩展: 适⽤于告警通知, 流程触发, 数据同步等场景

3.2.1 SkyWalking 的 WebHook 实现

SkyWalking 提供了WebHook的⽅式, 主要⽤于告警通知

3.2.1.1 告警触发与推送

当监控指标(如响应时间、错误率等)达到告警规则阈值时, SkyWalking会⽣成告警事件, ⾃动把告警信息封装为JSON格式, 通过HTTP的⽅式发送⾄预设的WebHook接收地址.

java 复制代码
#hooks:
#  webhook:
#    default:
#      is-default: true
#      urls:
#        - http://127.0.0.1/notify/
#        - http://127.0.0.1/go-wechat/

http的url就是在这里配置的

告警json格式

我们去查看一下这个文件List<org.apache.skywalking.oap.server.core.alarm.AlarmMessage>

点这个

点击star

跳转到github

然后搜索AlarmMessage

就是这个类,就是告警信息

scopeId、scope: 告警⽬标的监控范围, 参考org.apache.skywalking.oap.server.core.source.DefaultScopeDefine 中定义

DefaultScopeDefine 也可以搜索到

◦ scopeId、scope:就是说你是端点告警还是service告警还是实例告警

Name: 告警⽬标的名称,如服务名、端点名等

就这几个告警

id0: ⽬标实体的主要ID,通常是数据库中的主键

◦ id1: ⽬标实体的次要ID(可选),⽤于更精确的标识

ruleName: 触发告警的规则名称---》alarm-settings.yml中的

◦ alarmMessage: 具体的告警消息内容

◦ startTime: 告警触发的时间戳(毫秒)

◦ 标签: 标签列表,包含与告警相关的附加信息

3.2.1.2 WebHook配置

在 alarm-settings.yml 中定义 WebHook 地址及关联规则

java 复制代码
hooks:
 webhook:
   default:
     is-default: true
     urls:
       - http://127.0.0.1/notify/
       - http://127.0.0.1/go-wechat/

urls 为接收告警的HTTP端点

3.2.1.3 典型应⽤场景

◦ 集成第三⽅系统: 推送告警⾄钉钉, 企业微信、⻜书, 邮箱等协作⼯具.

◦ ⾃动化运维: 触发运维脚本(如⾃动扩容)或联动故障管理系统.

◦ 数据聚合分析: 将告警事件转发⾄⼤数据平台进⾏统计分析

3.2.2 WebHook实践

3.2.2.1 配置WebHook

配置 alarm-settings.yml , 然后重启

我们再来创建一个专门用于告警的应用程序

java 复制代码
hooks:
 webhook:
   default:
     is-default: true
     urls:
       - http://127.0.0.1:8084/alarm/handler
3.2.2.2 接⼝开发
java 复制代码
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
            </plugin>

        </plugins>
    </build>
java 复制代码
server:
  port: 8084
logging:
  pattern:
    dateformat: HH:mm:ss:SSS
java 复制代码
@RestController
@RequestMapping("/alarm")
public class AlarmController {
    @RequestMapping("/handler")
    public String handler(){

    }
}
java 复制代码
@Setter
@Getter
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 List<Tag> tags;
    private long startTime;
    private transient int period;
    private Set<String> hooks = new HashSet<>();
    private String expression;
    private JsonObject mqeMetricsSnapshot;
}

给我们的告警信息是一个AlarmMessage 的json格式

所以这个类也要创建

java 复制代码
@Setter
@Getter
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 List<Tag> tags;
    private long startTime;
    private transient int period;
    private String expression;
}

我们创建的删除了一些属性

说错了,这个才是给我们的json里面的内容

所以要含有tags,全部属性都要含有

这个Tag怎么处理呢

搜索Tag.java

还是仿照官方的

java 复制代码
@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 List<Tag> tags;
    private long startTime;
    private transient int period;
    private String expression;
    
    @Data
    public class Tag {
        private String key;
        private String value;
    }
}

这样就OK了

java 复制代码
@Slf4j
@RestController
@RequestMapping("/alarm")
public class AlarmController {
    @RequestMapping("/handler")
    public String handler(@RequestBody AlarmMessage alarmMessage){
        log.info("接收到告警,alarmMessage:{}",alarmMessage);
        return "接收到告警"; 
    }
}

然后SkyWalking也要重启

然后jmeter跑起来

我们等待UI界面,直到UI界面出现最新的告警信息---》只要第一个出来了,后面就很快了

这个17:21的是十几分钟以前的

OK,新的告警信息来了

我们看到error了,说json解析异常

他说接收到的json是一个数组

java 复制代码
    @RequestMapping("/handler")
    public String handler(@RequestBody List<AlarmMessage> alarmMessageList){
        log.info("接收到告警,alarmMessageList:{}",alarmMessageList);
        return "接收到告警";
    }

新告警又来了

也是打印出来了

3.2.3 配置126网易邮件告警

SkyWalking的WebHook功能, 会通过HTTP的⽅式, 把告警信息发送⾄预设的WebHook接收地址, 我们可以借此功能在这个接⼝⾥实现发送邮件或者短信等功能, 从⽽达到告警主动通知.

springboot发送邮件

java 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
java 复制代码
spring:
  mail:
    # 指定邮件服务器地址
    host: smtp.qq.com

这个是qq邮箱

java 复制代码
spring:
  mail:
    # 指定邮件服务器地址
    host: smtp.126.com

这个是126邮箱

手机下载网易邮箱,注册网易免费邮箱一个就可以了

java 复制代码
spring:
  mail:
    # 指定邮件服务器地址
    host: smtp.126.com

网易邮箱

然后是申请授权码了

设置--》点击pop3

然后我们开启pop3/SMTP

这样就会出现了

java 复制代码
spring:
  mail:
    # 指定邮件服务器地址
    host: smtp.126.com
    # 登录账户
    username: xxxxx@126.com
    # 授权码
    password: "xxxxxxxx"
    # 端口
    port: 465
    # 默认编码
    default-encoding: UTF-8
    # 使用的协议
    protocol: smtps
    # 其他的属性
    properties:
      "mail.smtp.connectiontimeout": 5000
      "mail.smtp.timeout": 3000
      "mail.smtp.writetimeout": 5000
      "mail.smtp.auth": true
      "mail.smtp.starttls.enable": true
      "mail.smtp.starttls.required": true

只需要改账户名授权码和host就可以了

这里有测试代码,我们直接复制就可以了

java 复制代码
@SpringBootTest
class AlarmServiceApplicationTest {

    @Autowired
    JavaMailSender javaMailSender;

    @Test
    void sendEmail() throws Exception{
        // 创建一个邮件消息
        MimeMessage message = javaMailSender.createMimeMessage();

        // 创建 MimeMessageHelper
        MimeMessageHelper helper = new MimeMessageHelper(message, false);

        // 发件人邮箱和名称
        helper.setFrom("ck_yyds@126.com", "springdoc");
        // 收件人邮箱
        helper.setTo("ck_yyds@126.com");
        // 邮件标题
        helper.setSubject("Hello");
        // 邮件正文,第二个参数表示是否是HTML正文
        helper.setText("Hello <strong> World</strong>!", true);

        // 发送
        javaMailSender.send(message);
    }

}

这样就成功了

我们分装一下发送邮件的方法

java 复制代码
    @Autowired
    private MailProperties mailProperties;

我们注入这个

这个类MailProperties 会读取邮件的配置信息

这些都是默认的配置可以不写

java 复制代码
spring:
  mail:
    # 其他的属性
    properties:
      "personal": "ck告警系统" #发送人姓名
      "subject": "告警通知"#邮件标题

我们设置这个

java 复制代码
@Slf4j
@Configuration
public class Mail {
    @Autowired
    private JavaMailSender javaMailSender;

    @Autowired
    private MailProperties mailProperties;

    public void sendEmail(String to,String content)  {
        try {
            // 创建一个邮件消息
            MimeMessage message = javaMailSender.createMimeMessage();

            // 创建 MimeMessageHelper
            MimeMessageHelper helper = new MimeMessageHelper(message, false);

            String from = mailProperties.getUsername();
            // 发件人邮箱和名称
            helper.setFrom(from, mailProperties.getProperties().getOrDefault("personal",from));//如果没有获取到personal的值,就是用默认值from
            // 收件人邮箱
            helper.setTo(to);
            // 邮件标题
            helper.setSubject(mailProperties.getProperties().getOrDefault("subject","告警通知"));
            // 邮件正文,第二个参数表示是否是HTML正文
            helper.setText(content, true);

            // 发送
            javaMailSender.send(message);
        }catch (Exception e){
            log.error("邮件发送失败,e:{}",e);
        }

    }
}

这样就分装好了

java 复制代码
    @Autowired
    private Mail mail;
java 复制代码
    @RequestMapping("/handler")
    public String handler(@RequestBody List<AlarmMessage> alarmMessageList){
        log.info("接收到告警,alarmMessageList:{}",alarmMessageList);
        mail.sendEmail("ck_yyds@126.com",buildContent(alarmMessageList));
        return "接收到告警";
    }

    private String buildContent(List<AlarmMessage> alarmMessageList) {
        StringBuilder builder = new StringBuilder();
        builder.append("告警详情:");
        for (AlarmMessage message:alarmMessageList){
            builder.append("<br/>scopeId:").append(message.getScopeId())
                    .append("<br/>scope:").append(message.getScope())
                    .append("<br/>Name:").append(message.getName())
                    .append("<br/>Id0:").append(message.getId0())
                    .append("<br/>RuleName:").append(message.getRuleName())
                    .append("<br/>AlarmMessage:").append(message.getAlarmMessage())
                    .append("<br/>StartTime:").append(message.getStartTime())
                    .append("<br/>-------------------------");
        }
        return builder.toString();
    }

然后启动等待告警

3.3 接入飞书

SkyWalking的WebHook功能, 也集成了第三⽅系统, 可以通过简单的配置, 推送告警⾄Slack, 企业微信, 钉钉, ⻜书等.

接入飞书

钉钉和飞书是一样的

微信只支持企业微信

钉钉

  1. 先注册飞书账号
  2. 创建机器⼈
    打开⻜书群组 → 点击右上⻆「设置」→「群机器⼈」→「添加机器⼈」→ 选择「⾃定义机器⼈」
    配置机器⼈名称, 获取⽣成的 Webhook URL(格式为 https://open.feishu.cn/openapis/bot/v2/hook/xxxxxx )

随便找一个群,点击设置

里面有一个群机器人

点击添加机器人

选择自定义机器人

成功之后会有一个webhook的地址--》记录下来

设置这个的话,发送请求就必须要有签名校验了

直接复制这个配置就可以了

但是还要加上第一级:hooks

java 复制代码
hooks:
  feishu:
    default:
      is-default: true
      text-template: |-
        {
          "msg_type": "text",
          "content": {
            "text": "Apache SkyWalking Alarm: \n %s."
          },
          "ats":"feishu_user_id_1,feishu_user_id_2"
        }      
      webhooks:
      - url: https://open.feishu.cn/open-apis/bot/v2/hook/dummy_token
        secret: dummysecret

ats这个是配置具体的userId,就是通知指定的一个人

这里的url改为刚刚添加机器人的webhook地址

secret就改为刚刚的签名

没有设置签名校验的话,secret就不用配置

然后就是重启SkyWalking

初始化慢慢等待吧


新告警来了,飞书也来消息了

如果想使用更加个性化的模版的话,SkyWalking就不行了

可以点击这个链接

在群组中使用机器人

总结

相关推荐
申阳8 小时前
Day 2:我用了2小时,上线了一个还算凑合的博客站点
前端·后端·程序员
清心歌8 小时前
Spring AI Alibaba 【四】
java·后端
bbmmqq8 小时前
根据角色ID获取完整角色信息(异步)
数据库·spring·oracle
一个儒雅随和的男子8 小时前
Redis性能调优指南
数据库·redis·spring
老华带你飞8 小时前
动漫资讯|基于Springboot的动漫交流网站设计与实现(源码+数据库+文档)
java·数据库·spring boot·后端·论文·毕设·国产动漫网站
爱分享的鱼鱼8 小时前
Java实践之路(一):记账程序
前端·后端
rengang668 小时前
105-Spring AI Alibaba Module RAG 使用示例
java·人工智能·spring·rag·spring ai·ai应用编程
有意义8 小时前
Spring Boot 项目中部门查询功能实现与依赖注入优化
后端·设计模式
鬼火儿8 小时前
15.<Spring Boot 日志>
java·后端