QuXiao's Blog

Life && Tech && Thoughts

Open-Falcon 源码阅读(五)其它模块 & 特点总结

Written on

其它模块

其它模块基本上逻辑比较简单,或者不涉及核心的逻辑,这里就简单介绍下。

Aggregate

Aggregate模块是一个集群聚合模块。聚合某集群下的所有机器的某个指标的值,提供一种集群视角的监控体验。 听起来挺高大上的,其实这就是一个离线模块,定期从数据库中读取需要进行聚合的指标,然后定期从Graph模块读取各个基础指标,经过计算之后再将新的『复合』指标写入到Graph中。这个功能,其实是为了解决上面介绍Judge的章节中无法判断类似错误率、可用率这样的指标的问题。

聚合指标数据

数据表结构如下:

CREATE TABLE cluster
(
  id          INT UNSIGNED   NOT NULL AUTO_INCREMENT,
  grp_id      INT            NOT NULL,
  numerator   VARCHAR(10240) NOT NULL,
  denominator VARCHAR(10240) NOT NULL,
  endpoint    VARCHAR(255)   NOT NULL,
  metric      VARCHAR(255)   NOT NULL,
  tags        VARCHAR(255)   NOT NULL,
  ds_type     VARCHAR(255)   NOT NULL,
  step        INT            NOT NULL,
  last_update TIMESTAMP      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  creator     VARCHAR(255)   NOT NULL,
  PRIMARY KEY (id)
)

Aggregate模块定期读取这张表,获知需要聚合哪些主机组的哪些指标,其中,endpoint/metric/tags/ds_type/step都是查询参数。

聚合指标的计算形式是:numerator/denominator,其中,numerator以及denominator的形式有三种:

  1. 分子或者分母上面指标的个数,用$#表示
  2. 整数:例如200
  3. 多个指标的表达式,例如:$(cpu.busy)+$(cpu.idle)-$(cpu.nice)-$(cpu.guest)

看了numerator以及denominator的表达式解析,也没有用什么词法语法树,直接就是字符串查找和切割,够简陋的……不过话说回来,够用就行。

func parse(expression string, needCompute bool) (operands []string, operators []uint8) {
        if !needCompute {
                return
        }

        // e.g. $(cpu.busy)+$(cpu.idle)-$(cpu.nice)-$(cpu.guest)
        //      xx          --          --          --         x
        newExpression := expression[2 : len(expression)-1]
        arr := strings.Split(newExpression, "$(")
        count := len(arr)
        if count == 1 {
                operands = append(operands, arr[0])
                return
        }

        if count > 1 {
                for i := 0; i < count; i++ {
                        item := arr[i]
                        length := len(item)
                        if i == count-1 {
                                operands = append(operands, item)
                                continue
                        }
                        operators = append(operators, item[length-1])
                        operands = append(operands, item[0:length-2])
                }
        }

        return
}

Alarm

Alarm是一个发送告警的模块。Judge把报警event写入redis,Alarm从redis读取event,做相应处理,可能是发报警短信、邮件,可能是callback某个http地址。生成的短信、邮件写入queue,Sender模块专门负责来发送。

在Alarm模块中,告警事件(event)分为高优先级和低优先级,低优先级事件会进行合并,高优先级不进行合并,并且只有当优先级足够高的时候,才会发送短信。默认配置是P0/P1不合并,收到之后直接发出;>=P2做报警合并。

Alarm模块收到告警事件的处理流程如下:

高优先级

  1. 从redis list中获取事件
  2. 将事件记录在map(g.Events, event id -> event)中
  3. 处理callback
  4. 根据UIC获取这个team的手机和邮箱列表
  5. 生成短信和邮件内容
  6. 如果优先级 < 3,则发送短信 (设置的比较死板)
  7. 将内容写入『发送list』(alarm模块只负责将需要发送的短信/邮件push至redis中,使用方自行决定如何发送)

低优先级(基本同上,只是会对短信/邮件进行合并)

  1. 前面相同步骤,略
  2. 通过UIC获得这个team的成员
  3. 对于每一个用户,将内容push至一个『中间list』
  4. 每一分钟将『中间list』中的短信和邮件进行合并,然后再写入『发送list』

Nodata

Nodata用于检测监控数据的上报异常。Nodata和实时报警Judge模块协同工作,过程为: 配置了Nodata的采集项超时未上报数据,Nodata生成一条默认的模拟数据;用户配置相应的报警策略,收到mock数据就产生报警。采集项上报异常检测,作为Judge模块的一个必要补充,能够使Judge的实时报警功能更加可靠、完善。 Nodata模块的运行机制与Aggregate类似,也是离线去读取配置,根据配置过Nodata告警的数据,如果没有发现数据就发送一份mock数据给Graph。

但是,Nodata对于以下情况会进行特殊处理:

  1. 由于核心网络故障,导致大部分的采集项上报异常
  2. 由于falcon自身服务故障,导致大量的采集项上报异常

这两类情况可能会引发大面积的告警,为了避免告警风暴的发生,Nodata采用了『值检测』方法。Nodata服务启动时,我们会为它配置一个阻塞阈值,nodata服务实时计算当前处于接收超时状态的监控数据项的百分比(简称异常百分比),然后这个用异常百分比与预先配置的阻塞阈值进行比较。如果异常百分比大于阻塞阈值,nodata服务就会停止发送mock数据;反之,如果异常百分比不大于阻塞阈值,nodata服务则正常发送mock数据。

Open-Falcon特点总结

数据采集

Agent能够采集服务器的基础指标,这是必需的功能。不过,Open-Falcon Agent精简了一些基础指标的采集,例如端口、磁盘空间、进程数等指标,如果都采集的话,上报的数据会比较大,也会浪费本地的计算资源。这里采用了同步配置的方法,Agent定期从服务端更新配置,只采集用户感兴趣的指标。另外,自定义的指标是通过执行外部脚本获取的(这种方式可能存在安全性问题),以及HTTP接口。Agent除了采集服务器自身的指标,还可以判断外部URL是否可以访问,不过这种的使用场景可以会比较少。此外,Agent还可以执行从信任IP发送的请求,在本地执行命令。

异常判断

Open-Falcon异常判断的最大特点就是将接收到数据作为判断策略的时机,一接收到数据,Judge模块就会找到对应的策略,判断数据是否有异常。因此,Open-Falcon中的策略都是判断『最近X次的数据』这种形式。这种方式的优点就是异常检测会很实时,缺点是无法支持类似『5分钟内的CPU idle』这类的策略,虽然可以通过上报频率转换为时间区间,但依然会比较麻烦。另外,偶尔的噪点值可能也会造成误判。

策略告警

在Open-Falcon中发生的告警事件,会归类成不同级别,主要分为高/低两种优先级,高优先级告警会单独告警;低优先级告警会进行告警合并,并且不会发送短信告警。其实,Open-Falcon中并没有真正实现发送告警的动作,只是将告警的内容发送至redis list,通过这种交互方式让使用方自行决定如何去发告警。

另外,在策略设置方面,Open-Falcon使用了策略模板的概念,模板可以继承自其它模板,这样策略的集合就可以十分灵活的组装起来。

传输与安全性

Open-Falcon中的几乎所有模块,几乎都采用了HTTP+RPC两种通信方式,HTTP主要是方便用户进行操作,模块间的通信采用的是RPC方式。 HTTP接口,主要用途是:

  • 标识模块的存活性、版本
  • 返回模块加载的数据
  • 对模块进行管理,例如reload配置文件
  • API接口

RPC接口,主要是提供模块本身暴露的API,直接使用golang的RPC库来实现。 安全性方面,因为Open-Falcon的定位是团队内部的监控系统,所以Agent的数据上报、以及其它模块之间的数据通信,都没有进行编码加密的操作。

自监控

监控系统监控了服务器,但是谁来监控监控系统?这就需要进行『自监控』了,Open-Falcon通过两种方式进行自监控:

  1. 使用一个简单的第三方模块来监控Open-Falcon模块

Open-Falcon使用一个叫AntEye的组件,多套AntEye组件监控一个网络分区中的Open-Falcon模块,如果发送异常就发出告警。

  1. 将Open-Falcon各个模块的内部指标上报

Open-Falcon Task模块,会收集各个模块的内部指标,经过一些格式的转换,上报至Open-Falcon本身,这样就可以在监控系统中进行指标查询、异常判断了。

-- EOF --

comments powered by Disqus