skywalking 6.0 告警模块代码解读
skywalking是一款针对分布式系统的APM(应用性能监控)系统,特别是针对微服务、cloud native和容器化(Docker, Kubernetes, Mesos)架构, 也可称为分布式追踪系统。主要提供以下功能:
- 分布式追踪和上下文传输
- 应用、实例、服务性能指标分析
- 根源分析
- 应用拓扑分析
- 应用和服务依赖分析
- 慢服务检测
- 性能优化
skywalking 6.0 架构图如下:
本文主要对skywalking6.0版本的告警模块源码进行解读,纯属个人理解,仅供参考。启用告警模块必须要在application.yml文件中增加“alarm/default”项才会在moduleMagager中加载告警的Module。
告警规则定义
告警的核心是由’ alarm-setting.yml’文件中配置的一组规则驱动的。报警规则(Rules)的定义分为两部分,
1. rules。定义了触发报警的度量和条件,该部分映射为AlarmRule类。
- [Rule name] 规则名称,唯一标识,需以“_rule”结尾
- indicator-name 指标名称, oal脚本中度量名称(oal相关源码会在后续文章中进行解读),必须在oal脚本定义过。
- include-names 适用该规则的实体名称,可配置系统名称、服务名称。不配置默认所有实体均适用。
- threshold 阀值。
- op 操作符,和阀值一起使用。
- period 时间段,告警规则检查的时间段。
- count 在period时间窗口内,达到op阀值的次数后即触发告警。
- silence-period 静默期,默认情况下与period相同,表示在静默期时间内,同样的告警只会触发一次。
- message 告警推送的消息
2. webhooks。触发告警规则后发送告警消息的服务端url列表。
规则配置示例如下:
rules: # Rule unique name, must be ended with `_rule`. service_resp_time_rule: indicator-name: service_resp_time op: ">" threshold: 1000 period: 10 count: 3 silence-period: 5 message: Response time of service {name} is more than 1000ms in 3 minutes of last 10 minutes. webhooks: - http://127.0.0.1/notify/ - http://127.0.0.1/go-wechat/ |
AlarmModule
org.apache.skywalking.oap.server.core.alarm.AlarmModule,实现 ModuleDefine 抽象类,Alarm管理器定义类。构造方法传入模块名name为 "alarm" 。
#services() 实现方法,返回 Service 类名:IndicatorNotify实现类NotifyHandler。
AlarmModuleProvider
org.apache.skywalking.oap.server.core.alarm.provider.AlarmModuleProvider,实现 ModuleProvider 抽象类,告警模块Alarm管理器组件服务提供者。
#name() 实现方法,返回组件服务提供者名 "default" 。
#module() 实现方法,返回组件类AlarmModule。
#requiredModules() 实现方法,返回依赖组件"core"。
#createConfigBeanIfAbsent()实现方法,返回AlarmSettings对象。
#prepare(Properties) 实现方法,执行准备阶段逻辑。
1、调用RulesReader.#readRules()方法读取“alarm-settings.yml”告警配置文件,将告警规则文件映射为Rules对象,其中Rules类由AlarmRule告警规则List和webhook发送地址List组成。
2、新建NotifyHandler(Rules)对象,并调用其#init(AlarmCallback...)方法,传入参数为新建AlarmStandardPersistence对象。
3、调用 #registerServiceImplementation(...) 方法,将NotifyHandler对象注册到 services
#start() 实现方法,执行启动阶段逻辑。目前是个空方法。
#notifyAfterCompleted() 实现方法,执行启动完成逻辑。目前是个空方法。
IndicatorNotify
告警服务接口,继承了 Service 接口。定义了#notify(MetaInAlarm, Indicator)方法。
NotifyHandler
告警服务的实现类,实现了 IndicatorNotify接口。
属性:
AlarmCore 属性:告警模块核心处理类
Rules 属性:Rules类由AlarmRule告警规则List和webhook发送地址List组成。
方法:
#构造方法:Rules属性由参数传入,并由Rules作为参数创建AlarmCore对象,请参考“6.AlarmCore”构造方法。
#notify(MetaInAlarm, Indicator) 实现方法,判断入参MetaInAlarm范围,只处理scope为Service,ServiceInstance,Endpoint的数据,其他数据不进行告警处理。
1、调用AlarmCore. #findRunningRule(String),由入参MetaInAlarm指标名称获取匹配的RunningRule实例List。
2、若第1步返回的List不为空,对List<RunningRule>元素分别调用RunningRule.#in(MetaInAlarm, Indicator)方法。请参考RunningRule类#in方法解析。
#init(AlarmCallback...) 方法,初始化
1、将输入参数转换为List<AlarmCallback>
2、用Rules. webhook地址创建WebhookCallback对象加入List<AlarmCallback>中
3、调用AlarmCore.#start(List<AlarmCallback>)方法持久化数据并通过webHook发送消息,请参考AlarmCore类#start方法解析。
AlarmCore
属性:
Map<String, List<RunningRule>> runningContext属性:指标名称IndicatorName与List<RunningRule>对应的Map。
LocalDateTime lastExecuteTime属性:记录最后执行时间。
方法:
#构造方法:由参数Rules创建AlarmCore对象。
逐笔扫描入参Rules对象的告警规则(AlarmRule)List,根据List元素(AlarmRule对象)创建RunningRule实例,并根据指标名称IndicatorName分组放入runningContext属性中。
#findRunningRule(String) 方法:由指标名称indicatorName获取List<RunningRule>。
#start(List<AlarmCallback>) 方法:
新建周期持久化数据并通过webHook发送消息线程池任务,代码解析如下:
1、初始化最后执行时间(lastExecuteTime属性)为当前系统时间。
2、调用Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate方法新建每10秒定期执行的单线程、延迟10秒的线程池任务,任务流程如下:
1、计算任务最后执行时间(lastExecuteTime属性)与当前任务执行时间(checkTime取当前系统时间)之间相差的分钟数
2、若分钟数不大于0,则本次任务不做任何事。
3、若分钟数大于0,则逐笔操作runningContext的值List<RunningRule>的每个元素,首先调用RunningRule类的#moveTo方法移动其时间窗口到当前任务执行时间(checkTime)。然后判断当前任务执行时间(checkTime)的秒数是否超过15,若超过15秒,则调用RunningRule类的#check()方法生成告警信息List<AlarmMessage>,再判断生成的告警信息是否为空,不为空则对入参List<AlarmCallback>逐笔调用AlarmCallback.#doAlarm方法推送告警信息并将任务最后执行时间(lastExecuteTime属性)更新为当前任务执行时间(checkTime,秒数设为00)。
RunningRule
分别对不同的告警规则的范围内的实例对象进行是否触发告警规则的判断并记录。
属性:
String ruleName、int period、String indicatorName、Threshold threshold、OP op、int countThreshold、int silencePeriod、List<String> includeNames对应规则定义中rules各属性,此处不做详解。
Map<MetaInAlarm, Window> windows属性:记录告警实体数据(范围为:Service,ServiceInstance, Endpoint)与告警规则配置中时间窗口内每分钟应用触发告警的情况。
volatile IndicatorValueType valueType属性:告警规则指标值类型,包括Long,Int,Double
Scope targetScope:目标范围,暂未用
AlarmMessageFormatter formatter属性:告警消息的格式化类,代码比较简单,此处不做详解。
方法:
构造方法:根据输入参数AlarmRule初始化各属性。
#in(MetaInAlarm meta, Indicator indicator):
1、判断入参meta告警实体数据的指标名称是否等于当前规则的属性indicatorName指标名称,若不相同则直接返回。
2、判断入参meta告警实体数据的名称是否在当前规则的include-names名称名单内,若不在,则返回。若当前规则的include-names名称名单为空,则不判断,默认所有实体名称均适用。
3、若指标值类型valueType属性为空,则从入参的indicator指示器数据中获取赋值。
4、当指标值类型valueType属性不为空时,若windows中不存在入参meta为key的元素,则新建Window对象,并加入到windows属性中。最后调用Window内部类的#add方法将入参数据indicator加入到相应的Window对象中。
#moveTo(LocalDateTime targetTime):根据传入时间,调用Window内部类的#moveTo方法移动windows属性所有元素的时间窗口。
#check():逐笔循环windows属性,调用Window内部类的#checkAlarm()方法生成AlarmMessage对象,若#checkAlarm()方法返回的不是NoAlarm对象,则将AlarmMessage对象加入到告警信息List<AlarmMessage>中,最后返回该告警信息List。
内部类Window:
LocalDateTime endTime属性:当前window的结束时间
int period属性:告警规则rules的时间段
int counter属性:记录在period时间窗口内,达到op阀值的次数。初始化为0
int silenceCountdown属性:记录静默期剩余量,初始值为告警规则rules的silence-period 静默期。初始化为-1
LinkedList<Indicator> values属性:长度为period,记录endTime前period分钟的时间窗口内每分钟的Indicator实例数据。
ReentrantLock lock:可重入锁,用于线程同步控制。
构造方法:
#moveTo(LocalDateTime) :使用可重入锁进行线程同步控制,根据传入时间移动values时间窗口。
-
- 若endTime为null,初始化values为长度period,值为null的list,并将传入的时间赋给endTime。
- 若endTime不为null,若传入时间在endTime时间后则不做任何事,返回。再判断传入时间与endTime的分钟差,若分钟差大于period,则重新初始化values属性为长度period,值为null的list;否则移动values数据时间窗口到传入时间并设置window的结束时间endTime为传入时间。
#add(Indicator):将Indicator实例数据根据其时间戳加入到values中相应时间的位置上。
#isMatch():该方法主要进行是否触发告警规则的判断。
逐笔轮询values数据,Indicator实例的值与RunningRule类的阀值threshold进行比较(数据类型是RunningRule类的valueType属性,比较的操作符是RunningRule类的op属性),若(Indicator实例的值op threshold)成立,则匹配数量matchCount+1,判断完所有的values数据后,若匹配数量matchCount是否达到或超过RunningRule类的countThreshold属性,则返回true;否则返回false。
#checkAlarm():
- 调用isMatch()方法,返回true进入步骤2,否则进入步骤3。
- 若isMatch()返回true,则表明该时间窗口内的链路数据触发了告警规则。首先属性counter+1,再判断counter是否大于等于RunningRule类的countThreshold属性(该处判断与isMatch方法中的判断冲突,不合理?)且静默期剩余量silenceCountdown小于1,则重新赋值静默期剩余量silenceCountdown为规则的初始静默期silencePeriod,并返回新建的AlarmMessage告警信息对象数据。否则将静默期剩余量silenceCountdown减1。
- 若isMatch()返回false,则表明该时间窗口内的链路数据未触发告警规则。将静默期剩余量silenceCountdown减1,若属性counter大于0,则将counter减1。返回NoAlarm对象。
AlarmEntrance
告警服务的数据入口类。该类主要介绍#forward方法。
# forward(Indicator indicator):
- 首先判断moduleManager中有无注册过“core”组件。
- 初始化,实例化ServiceInventoryCache,ServiceInstanceInventoryCache, EndpointInventoryCache,IndicatorNotify。
- 从输入参数indicator实例中获取AlarmMeta告警元素信息,根据告警元素信息的的范围分别实例化MetaInAlarm告警实体数据。
- 调用告警服务接口的#notify方法,调用参数为步骤3生成的MetaInAlarm实例和入参indicator。
AlarmNotifyWorker
skywalking流式处理的工作节点,继承了抽象类AbstractWorker,主要介绍#in方法
# in(Indicator indicator):首先判断输入参数是否是AlarmSupported的实例,若不是则不处理,若是则调用AlarmEntrance的#forward(indicator)方法。
AlarmCallback
告警信息持久化或推送的接口,只有一个方法#doAlarm(List<AlarmMessage> alarmMessage)用于处理告警信息,总共有两个类实现了该接口: AlarmStandardPersistence 类用于持久化告警信息;WebhookCallback类用于通过Webhook推送告警信息。