微服务链路追踪SkyWalking
链路追踪介绍
对于一个大型的几十个、几百个微服务构成的微服务架构系统,通常会遇到下面一些问题,比如:
 (1) 如何串联整个调用链路,快速定位问题?
 (2) 如何缕清各个微服务之间的依赖关系?
 (3) 如何进行各个微服务接口的性能分析?
 (4) 如何跟踪整个业务流程的调用处理顺序?
 
skywalking是什么
skywalking是一个国产开源框架,2015年由吴晟开源,2017年加入Apache孵化器。skywalking是分布式系统的应用程序性能监视工具,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。它是一款优秀的APM(Application Performance Management)工具,包括了分布式追踪、性能指标分析、应用和服务依赖分析等。
 官网:http://skywalking.apache.org/
 下载:http://skywalking.apache.org/downloads/
 Github:http://github.com/apache/skywalking
 文档:http://skywalking.apache.org/docs/main/v8.4.0/readme/
 中午文档:http://skyapm.github.io/document-cn-translation-of-skywalking/
1.链路追踪框架对比
 (1) Zipkin是Twitter开源的调用链分析工具,目前基于springcloud sleuth得到了广泛的使用,特点是轻量,使用部署简单。
 (2) Pinpoint是韩国人开源的基于字节码注入的调用链分析,以及应用监控分析工具,特点是支持多种插件,UI功能强大,接入端无代码侵入。
 (3) SkyWalking是本土开源的基于字节码注入的调用链分析,以及应用监控分析工具,特点是支持多种插件,UI功能较强,接入端无代码侵入。 目前已加入Apache孵化器。
 (4) CAT是大众点评开源的基于编码和配置的调用链分析,应用监控分析,日志采集,监控报警等一系列的监控平台工具。
2.基本原理
| 项目 | CAT | Zipkin | SkyWalking | 
|---|---|---|---|
| 调用链可视化 | 有 | 有 | 有 | 
| 聚合报表 | 非常丰富 | 少 | 较丰富 | 
| 服务依赖图 | 简单 | 简单 | 好 | 
| 埋点方式 | 侵入式 | 侵入式 | 非侵入,字节码增强 | 
| VM监控指标 | 好 | 无 | 有 | 
| 支持语言 | java/.net | 丰富 | java/.net/Nodejs/php/go | 
| 存储机制 | mysql(报表)、本地文件/HDFS(调用链) | 内存、es、mysql等 | H2、es | 
| 社区支持 | 主要在国内 | 国外主流 | Apache支持 | 
| 使用案例 | 美团、携程、陆金所 | 京东、阿里定制后不开源 | 华为、小米、当当、微众银行 | 
| APM | 是 | 否 | 是 | 
| 开发基础 | eBay | Google Dapper | Google Dapper | 
| 是否支持webflux | 否 | 是 | 是 | 
| Github stars(2019.12) | 12.3K | 12.2K | 11.8K | 
3.性能对比
 模拟了三种并发用户:500,750,1000。使用jmeter测试,每个线程发送30个请求,设置思考时间为10ms。使用的采用率为1,即100%,这边与生产可能有差别。pinpoint默认的采样率为20,即50%,通过设置agent的配置文件改为100%。zipkin默认也是1。组合起来,一共有12种。下面看下汇总表:
 
 从上表可以看出,在三种链路监控组件中,skywalking的探针对吞吐量的影响最小,zipkin的吞吐量居中。pinpoint的探针对吞吐量的影响较为明显,在500并发用户时,测试服务的吞吐量从1385降低到774,影响很大。然后再看下CPU和memory的影响,在内部服务器进行的压测,对CPU和memory的影响都差不多在10%之内。
4.SkyWalking主要功能特性
 (1) 多种监控手段,可以通过语言探针和service mesh获得监控的数据;
 (2) 支持多种语言自动探针,包括Java、.Net Core 和 Node.js;
 (3) 轻量高效,无需大数据平台和大量的服务器资源;
 (4) 模块化。UI、存储、集群管理都有多种机制可选;
 (5) 支持告警;
 (6) 优秀的可视化解决方案;
SkyWalking环境搭建部署

 (1) SkyWalking agent和业务系统绑定在一起,负责收集各种监控数据
 (2) SkyWalking oapservice是负责处理监控数据的,比如接受SkyWalking agent的监控数据,并存储在数据库中;接受SkyWalking webapp的前端请求,从数据库查询数据,并返回数据给前端。SkyWalking oapservice通常以集群的形式存在。
 (3) SkyWalking webapp,前端界面,用于展示数据。
 (4) 用于存储监控数据的数据库,比如mysql、elasticsearch等。
1.下载SkyWalking
 下载地址:http://skywalking.apache.org/downloads/
 
 目录结构:
 
1》webapp:UI前端(web监控页面)的jar包和配置文件;
 2》oap-libs:后台应用的jar包,以及它的依赖jar包,里边有一个server-starter-.jar就是启动程序;
 3》config:启动后台应用程序的配置文件,是存放应用的各种配置
 4》bin:各种启动脚本,一般使用脚本startup.来启动web页面和对应的后台应用;
 (1) oapService.:默认使用的后台程序的启动脚本;(使用的是默认模式启动,还支持其他模式,各模式区别见启动模式)
 (2) oapServiceInit.:使用init模式启动;在此模式下,OAP服务器启动以执行初始化工作,然后退出
 (3) oapServiceNoInit.:使用no init模式启动;在此模式下,OAP服务器不进行初始化。
 (4) webappService.:UI前端的启动脚本;
 (5) startup.:组合脚本,同时启动oapService.、webappService.*脚本;
 5》agent
 (1) skywalking-agent.jar:代理服务jar包
 (2) config:代理服务启动时使用的配置文件
 (3) plugins:包含多个插件,代理服务启动时会加载该目录下的所有插件(实际是各种jar包)
 (4) optional-plugins:可选插件,当需要支持某种功能时,比如SpringCloud Gateway,则需要把对应的jar包拷贝到plugins目录下;
2.搭建SkyWalking OAP服务
 1》启动脚本
 bin/startup.sh
 
 2》日志信息存储在logs目录
 
3》启动成功后会启动两个服务,一个是skywalking-oap-server,一个是skywalking-web-ui
 skywalking-oap-server服务启动后会暴露11800和12800两个端口,分别为收集监控数据的端口11800和接受前端请求的端口12800,修改端口可以修改config/application.yml
 
 4》skywalking-web-ui服务会占用8080端口,修改端口可以修改webapp/webapp.yml
 
 5》浏览web页面:http://localhost:8868/
 
 页面的右下角可以中英文切换,可以切换选择要展示的时间区间的跟踪数据。
3.SkyWalking中三个概念
 服务(Service):表示对请求提供相同行为的一系列或一组工作负载,在使用Agent时,可以定义服务的名字;
 服务实例(Service Instance):上述的一组工作负载中的每一个工作负载称为一个实例,一个服务实例实际就是操作系统上的一个真实进程;
 端点(Endpoint):对于特定服务所接收的请求路径,如HTTP的URL路径和gRPC的服务的类名 + 方法签名;
 
SkyWalking跨多个微服务跟踪
SkyWalking跨多个微服务跟踪,只需要每个微服务启动时添加javaagent参数即可。
 测试:
 启动微服务api-gateway、alibaba-order-seata、alibaba-stock-seata,配置skywalking的jvm参数
-javaagent:D:\server\apache-skywalking-apm-bin-es7\agent\skywalking-agent.jar
-DSW_AGENT_NAME=alibaba-stock-seata
-DSW_AGENT_COLLECTOR_BACKEEND_SERVICES=127.0.0.1:11800
 
请求地址:http://localhost:8088/order/add
SkyWalking UI介绍
SkyWalking UI页面功能
 1.菜单栏
 
 仪表盘:查看被监控服务的运行状态;
 拓扑图:以拓扑图的方式展现服务之间的关系,并以此为入口查看相关信息;
 追踪:以接口列表的方式展现,追踪接口内部调用过程;
 性能剖析:对端点进行采样分析,并可查看堆栈信息;
 告警:触发告警的告警列表,包括服务失败率,请求超时等;
 自动刷新:刷新当前页面数据内容;
2.控制栏
 
 第一栏:不同内容主题的监控面板,应用性能管理/数据库/容器等;
 第二栏:操作,包括编辑/导出当前数据/倒入展示数据/不同服务端点筛选展示;
 第三栏:不同维度展示,全局/服务/实例/端点;
3.展示栏
 Global全局维度
 第一栏:Global、Service、Instance、Endpoint不同展示版面;
 Service load:服务每分钟请求数;
 Slow Service:慢响应服务,单位ms;
 Un-Health Services(Apdex):Apdex性能指标,1为满分;
 Slow Endpoint:慢响应端点,单位ms;
 Global Response Latency:百分比响应延时,不同百分比的延时时间,单位ms;
 Global Heatmap:服务响应时间热力分布图,根据时间段内不同响应时间的数量显示颜色深度;
 底部栏:展示数据的时间区间,点击可以调整;
 
4.Service服务维度
 Service Apdex(数字):当前服务的评分;
 Service Apdex(折线图):不同时间的Apdex评分;
 Service Avg Response Times:平均响应延时,单位ms;
 Service Response Time Percentile:百分比响应延时;
 Successful Rate(数字):请求成功率;
 Successful Rate(折线图):不同时间的请求成功率;
 Service Load(数字):每分钟请求数;
 Service Load(折线图):不同时间的每分钟请求数;
 Service Instances Load:每个服务实例的每分钟请求数;
 Show Service Instance:每个服务实例的最大延时;
 Service Instance Successful Rate:每个服务实例的请求成功率;
 
 5.Instance服务维度
 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线程数;
 还有几个是.NET的,类似于JVM虚拟机,暂时不做说明;
 
 6.Endpoint端点(API)维度
 Endpoint Load in Current Service:每个端点的每分钟请求数;
 Slow Endpoint in Current Service:每个端点的最慢请求时间,单位ms;
 Successful Rate in Current Service:每个端点的请求成功率;
 Endpoint Load:当前端点每个时间段的请求数据;
 Endpoint Avg Response Time:当前端点每个时间段的请求行响应时间;
 Endpoint Response Time Percentile:当前端点每个时间段的响应时间占比;
 Endpoint Successful Rate:当前端点每个时间段的请求成功率;
 
 7.拓扑图
 1》选择不同的服务关联拓扑图;
 2》查看单个服务相关内容;
 3》服务间连接情况;
 4》分组展示服务拓扑;
 
8.追踪
 左侧:api接口列表,红色-异常请求,蓝色-正常请求;
 右侧:api追踪列表,api请求连接各端点的先后顺序和时间;
 
SkyWalking接入微服务
1.Linux环境 – 通过jar包方式接入
 准备一个springboot程序,打成可执行jar包,写一个shell脚本,在启动项目的shell脚本上,通过 -javaagent参数进行配置SkyWalking Agent来跟踪微服务;
 startup.sh脚本:
#! /bin/sh
# SkyWalking Agent配置
export SW_AGENT_NAME=springboot-skywalking-demo  # Agent名字,一般使用'spring.application.name'
export SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 #配置Collector地址
export SW_AGENT_SPAN_LIMIT=2000  #配置链路的最大Span数量,默认为300
export JAVA_AGENT=-javaagent:/usr/local/soft/apache-skywalking-apm-bin-es7/agent/skywalking-agent.jar
java $JAVA_AGENT -jar springboot-skywalking-demo-0.0.1-SNAPSHOT.jar #jar启动
 
启动日志:
 
 等同于:
java -javaagent:/usr/local/soft/apache-skywalking-apm-bin-es7/agent/skywalking-agent.jar
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 
-DSW_AGENT_NAME=springboot-skywalking-demo -jar springboot-skywalking-demo-0.0.1-SNAPSHOT.jar
 
参数名对应agent/config/agent.config配置文件中的属性。
 属性对应的源码:org.apache.skywalking.apm.agent.core.Config.java
# The service name in UI
agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
# Backend service addresses.
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:127.0.0.1:11800}
 
我们也可以使用skywalking+配置文件中的配置名作为系统配置来进行覆盖。javaagent参数配置方式优先级更高
2.Windows环境 – 在IDEA中使用SkyWalking
 在运行的程序配置jvm参数;如下图所示:
 
# skywalking-agent.jar的本地磁盘的路径
-javaagent:D:\server\apache-skywalking-apm-bin-es7\agent\skywalking-agent.jar
# 在skywalking上显示的服务名
-DSW_AGENT_NAME=springboot-skywalking-demo
# skywalking的collector服务的IP及端口
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 
 
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES 可以指定远程地址,但是-javaagent必须绑定你本机物理路径的skywalking-agent.jar
注意:此处存在bug,跟踪链路不显示gateway
 拷贝agent/optional-plugins目录下的gateway插件到agent/plugins目录下
 
 
SkyWalking持久化跟踪数据
默认使用的H2数据库存储
 config/application.yml
 
 
 1.基于mysql持久化
 1》修改config目录下的application.yml,使用mysql作为持久化存储的仓库
 
2》修改mysql连接配置
 
 3》新建swtest数据库,swtest数据库中的表会在SkyWalking启动时自动生成
 
 4》启动SkyWalking服务
 
 启动时报下面异常:
 
 原因:因为oap-libs中没有jdbc驱动jar包。
 解决方法:在oap-libs文件夹中添加jdbc驱动jar包(mysql-connector-java-8.0.21.jar).
自定义SkyWalking链路追踪
如果我们希望对项目中的的业务方法,实现链路追踪,方便我们排查问题,可以使用如下的代码
 引入依赖:
<!--skywalking 工具类-->
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-trace</artifactId>
    <version>8.5.0</version>
</dependency>
 
1.@Trace将方法加入追踪链路
 如果一个业务方法想在ui界面的跟踪链路上显示出来,只需要在业务方法上加上@Trace注解即可
@Override
@Trace
public List<Order> all() throws InterruptedException {
    TimeUnit.SECONDS.sleep(2);
    return orderMapper.selectAll();
}
 
测试:
 
 2.加入@Tags或@Tag
 我们还可以为追踪链路增加其他额外的信息,比如记录参数和返回信息。实现方式:在方法上增加@Tag或者@Tags。
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    OrderMapper orderMapper;
    @Override
    @Trace
    @Tag(key = "getAll", value = "returnedObj")
    public List<Order> all() throws InterruptedException {
        TimeUnit.SECONDS.sleep(2);
        return orderMapper.selectAll();
    }
    @Override
    @Trace
    @Tags({@Tag(key = "param", value = "arg[0]"),
            @Tag(key = "get", value = "returnedObj")})
    public Order get(Integer id) {
        return orderMapper.selectByPrimaryKey(id);
    }
}
 
测试:
 
 
性能分析
SkyWalking的性能分析,在根据服务名称、端点名称,以及相应的规则建立了任务列表后,在调用了此任务列表的端点后。SkyWalking会自动记录,剖析当前端口,生成剖析结果,具体流程如图:
 
 
SkyWalking集成日志框架
logback官方配置
 
引入依赖
<!--apm-toolkit-logback-1.x-->
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-logback-1.x</artifactId>
    <version>8.5.0</version>
</dependency>
 
添加logback-spring.xml文件,并配置[%tid]占位符
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 引入Spring Boot 默认的logback XML 配置文件-->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <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>-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} [%tid] %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}</Pattern>
            </layout>
        </encoder>
    </appender>
    <!-- 设置Appender-->
    <root level="INFO">
        <appender-ref ref="console"/>
    </root>
</configuration>
 

 SkyWalking通过grpc上报日志(需要v8.4.0+)
 gRPC报告程序可以将收集到的日志转发到SkyWalking OAP服务器上
 logback-spring.xml中添加:
<!--v8.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} [%X{tid}] [%thread] %-5level %logger{36} -%msg%n</Pattern>
        </layout>
    </encoder>
</appender>
<!-- 设置Appender-->
<root level="INFO">
    <appender-ref ref="console"/>
    <appender-ref ref="grpc-log"/>
</root>
 
打开agent/config/agent.config配置文件,添加如下配置信息:
plugin.toolkit.log.grpc.reporter.server_host=${SW_GRPC_LOG_SERVER_HOST:127.0.0.1}
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}
 
以上配置是默认配置信息,agent与oap在本地的可以不配
| 配置名 | 解释 | 默认值 | 
|---|---|---|
| plugin.toolkit.log.transmit_formatted | 是否以格式化或未格式化的格式传输记录的数据 | true | 
| plugin.toolkit.log.grpc.reporter.server_host | 指定要向其报告日志数据的grpc服务器的主机 | 127.0.0.1 | 
| plugin.toolkit.log.grpc.reporter.server_port | 指定要向其报告日志数据的grpc服务器的端口 | 11800 | 
| plugin.toolkit.log.grpc.reporter.max_message_size | 指定grpc客户端要报告的日志数据的最大大小 | 10485760 | 
| plugin.toolkit.log.grpc.reporter.upstream_timeout | 客户端向上游发送数据时将超时多长时间。单位是秒 | 30 | 
Skywalking UI效果:
 
SkyWalking告警功能
SkyWalking告警功能是在6.x版本新增的,其核心由一组规则驱动,这些规则定义在config/alarm-setting.yml文件中,告警规则的定义分为两部分:
 1》告警规则:它们定义了应该如何触发度量警报,应该考虑什么条件。
 2》Webhook(网络钩子):定义当警告触发时,哪些服务端需要被告知
1.告警规则
 SkyWalking的发行版都会默认提供config/alarm-setting.yml文件,里面预先定义了一些常用的告警规则,如下:
 1》过去3分钟内服务平均响应时间超过1秒。
 2》过去2分钟服务成功率低于80%。
 3》过去3分钟内服务响应时间超过1s的百分比。
 4》服务实例在过去2分钟内平均响应时间超过1s,并且实例名称与正则表达式匹配。
 5》过去2分钟内端点平均响应时间超过1秒。
 6》过去2分钟内数据库访问平均响应时间超过1秒。
 7》过去2分钟内端点关系平均响应时间超过1秒。
 这些预定义的告警规则,打开config/alarm-setting.yml文件即可看到
告警规则配置项的说明:
 1》Rule name:规则名称,也是在告警信息中显示的唯一名称,必须以_rule结尾,前缀可自定义
 2》Metrics name:度量名称,取值为oal脚本中的度量名,目前只支持long、double和int类型。详见Official OAL script
 3》Include names:该规则作用于哪些实体名称,比如服务名,终端名(可选,默认为全部)
 4》Exclude names:该规则作不用于哪些实体名称,比如服务名,终端名(可选,默认为全部)
 5》Threshold:阈值
 6》OP:操作符,目前支持>,<,=
 7》Period:多久警告规则需要被核实一下,这是一个时间窗口,与后端部署环境时间相匹配
 8》Count:在一个Period窗口中,如果values超时Threshold值(按op),达到Count值,需要发送警报
 9》Sitence period:在时间N中触发报警后,在TN -> TN + period这个阶段不告警。默认情况下,它和Period一样,这意味着看相同的告警(在同一个Metrics name拥有相同的id)在同一个Ponod内只会触发一次
 10》message:告警信息
Webhook(网络钩子)
 Webhook可以简单理解为是一种Web层面的回调机制,通常由一些事件触发,与代码中的事件回调类似,只不过是Web层面的。由于是Web层面的,所以当事件发生时,回调的不再是代码中的方法或函数,而是服务接口。例如,在警告这个场景,警告就是一个事件,当该事件发生时,SkyWalking就会主动去调用一个配置好的接口,该接口就是所谓的Webhook。
 SkyWalking的告警消息会通过HTTP请求进行发送,请求方法为POST,Content-Type为application/json,其JSON数据是基于List<org.apache.skywalking.oap.server.core.alarm.AlarmMessage>进行序列化的。JSON数据示例:
[{
	"scopeId": 1, 
	"scope": "SERVICE",
	"name": "serviceA", 
	"id0": "12",  
	"id1": "",  
    "ruleName": "service_resp_time_rule",
	"alarmMessage": "alarmMessage xxxx",
	"startTime": 1560524171000
}, {
	"scopeId": 1,
	"scope": "SERVICE",
	"name": "serviceB",
	"id0": "23",
	"id1": "",
    "ruleName": "service_resp_time_rule",
	"alarmMessage": "alarmMessage yyy",
	"startTime": 1560524171000
}]
 
字段说明:
 1》scopeld、scope:所有可用的Scope详见org.apache.skywalking.oap.server.core.source.DefaultScopeDefine
 2》name:目标Scope的实体名称
 3》Id0:Scope实体的ID
 4》Id1:保留字段,目前暂未使用
 5》ruleName:告警规则名称
 6》alarmMessage:告警消息内容
 7》startTime:告警时间,格式为时间戳
告警信息在项目日志中输出
 1.在config/alarm-setting.yml中添加请求地址
 
 2.SwAlarmDTO实体类
public class SwAlarmDTO {
    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 transient boolean onlyAsCondition;
    @Data
    public static class Tag{
        private String key;
        private String value;
    }
    public int getScopeId() {
        return scopeId;
    }
    public void setScopeId(int scopeId) {
        this.scopeId = scopeId;
    }
    public String getScope() {
        return scope;
    }
    public void setScope(String scope) {
        this.scope = scope;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getId0() {
        return id0;
    }
    public void setId0(String id0) {
        this.id0 = id0;
    }
    public String getId1() {
        return id1;
    }
    public void setId1(String id1) {
        this.id1 = id1;
    }
    public String getRuleName() {
        return ruleName;
    }
    public void setRuleName(String ruleName) {
        this.ruleName = ruleName;
    }
    public String getAlarmMessage() {
        return alarmMessage;
    }
    public void setAlarmMessage(String alarmMessage) {
        this.alarmMessage = alarmMessage;
    }
    public List<Tag> getTags() {
        return tags;
    }
    public void setTags(List<Tag> tags) {
        this.tags = tags;
    }
    public long getStartTime() {
        return startTime;
    }
    public void setStartTime(long startTime) {
        this.startTime = startTime;
    }
    public int getPeriod() {
        return period;
    }
    public void setPeriod(int period) {
        this.period = period;
    }
    public boolean isOnlyAsCondition() {
        return onlyAsCondition;
    }
    public void setOnlyAsCondition(boolean onlyAsCondition) {
        this.onlyAsCondition = onlyAsCondition;
    }
    @Override
    public String toString() {
        return "SwAlarmDTO{" +
                "scopeId=" + scopeId +
                ", scope='" + scope + '\'' +
                ", name='" + name + '\'' +
                ", id0='" + id0 + '\'' +
                ", id1='" + id1 + '\'' +
                ", ruleName='" + ruleName + '\'' +
                ", alarmMessage='" + alarmMessage + '\'' +
                ", tags=" + tags +
                ", startTime=" + startTime +
                ", period=" + period +
                ", onlyAsCondition=" + onlyAsCondition +
                '}';
    }
}
 
3.controller类
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/alarm")
public class SwAlarmController {
    /**
     * 接收skywalking服务的告警通知并发送至邮箱
     *
     * 必须是post
     */
    @PostMapping("/receive")
    public void receive(@RequestBody List<SwAlarmDTO> alarmList){
        String content = getContent(alarmList);
        log.info("告警邮件已发送。。。" + content);
    }
    private String getContent(List<SwAlarmDTO> alarmList) {
        StringBuilder sb = new StringBuilder();
        for (SwAlarmDTO dto : alarmList) {
            sb.append("scopeId: ").append(dto.getScopeId())
                    .append("\nscope: " ).append(dto.getScope())
                    .append("\n目标 Scope 的实体名称: ").append(dto.getName())
                    .append("\nScope 实体的 ID:").append(dto.getId0())
                    .append("\nid1: ").append(dto.getId1())
                    .append("\n告警规则名称: ").append(dto.getRuleName())
                    .append("\n告警消息内容: ").append(dto.getAlarmMessage())
                    .append("\n告警时间: ").append(dto.getStartTime())
                    .append("\n标签: ").append(dto.getTags())
                    .append("\n\n-----------------\n\n");
        }
        return sb.toString();
    }
}
 
4.请求地址:http://localhost:8088/order/all
 5.SkyWalking UI界面
 
6.日志输出内容
 









