Open-falcon的graph组件应用rrd保留指标数据,rrd因为是本地存储,单机故障容易引起数据失落,Open-falcon提供了双写的逻辑(transfer双写2个graph节点)以加强可用性。
graph的指标数据的存储,可抉择不同的TSDB,比方InfluxDB、OpenTSDB等。
本文次要联合源码,介绍Open-falcon如何应用rrd保留和查问数据。
1. rrd文件的命名
rrd文件的命名形式:
md5=md5sum(metric, endpoint, tags)md5[0:2]_md5_dsType_step.rrd
看一下代码中的实现:
// RRDTOOL UTILS// 监控数据对应的rrd文件名称func RrdFileName(baseDir string, md5 string, dsType string, step int) string { return baseDir + "/" + md5[0:2] + "/" + md5 + "_" + dsType + "_" + strconv.Itoa(step) + ".rrd"}
md5的计算:将endpoint/metric/tags拼成string,而后md5sum(string)
//MD5的值func Checksum(endpoint string, metric string, tags map[string]string) string { pk := PK(endpoint, metric, tags) return Md5(pk)}func PK(endpoint, metric string, tags map[string]string) string { ret := bufferPool.Get().(*bytes.Buffer) ret.Reset() defer bufferPool.Put(ret) if tags == nil || len(tags) == 0 { ret.WriteString(endpoint) ret.WriteString("/") ret.WriteString(metric) return ret.String() } ret.WriteString(endpoint) ret.WriteString("/") ret.WriteString(metric) ret.WriteString("/") ret.WriteString(SortedTags(tags)) return ret.String()}
比方:trovedev这台机器的agent.alive的数据,该指标将被存储在be/be3cdc0fce0d498e19e2c6e9f1f338b6_GAUGE_60.rrd文件中:
# echo -n "trovedev/agent.alive" | md5sumbe3cdc0fce0d498e19e2c6e9f1f338b6 -# ls -alh be/be3cdc0fce0d498e19e2c6e9f1f338b6_GAUGE_60.rrd-rw-r--r-- 1 root root 70K 6月 2 09:43 be/be3cdc0fce0d498e19e2c6e9f1f338b6_GAUGE_60.rrd
2. rrd文件的创立
rrd文件被创立时,除了在本地磁盘上新建了一个rrd文件,还指定了指标的归档策略,也就是指定保留多久、如何聚合:
比方:原始的数据1min1个点,保留12hour;原始的数据每隔5min被聚合(AVERAGE)成1个点,原始的数据被删掉;
// modules/graph/rrdtool/rrdtool.goconst ( RRA1PointCnt = 720 RRA5PointCnt = 576 RRA20PointCnt = 504 RRA180PointCnt = 766 RRA720PointCnt = 730 )func create(filename string, item *cmodel.GraphItem) error { now := time.Now() start := now.Add(time.Duration(-24) * time.Hour) step := uint(item.Step) c := rrdlite.NewCreator(filename, start, step) c.DS("metric", item.DsType, item.Heartbeat, item.Min, item.Max) // 设置各种归档策略 // 1min1个点,存12hour c.RRA("AVERAGE", 0, 1, RRA1PointCnt) //RRA1PointCnt=720: 1min1个点,存720个点,即12hour // 5m1个点,存2d c.RRA("AVERAGE", 0, 5, RRA5PointCnt) //RRA5PointCnt=576:5min1个点, 存576个点,即2d c.RRA("MAX", 0, 5, RRA5PointCnt) c.RRA("MIN", 0, 5, RRA5PointCnt) // 20m1个点,存7d c.RRA("AVERAGE", 0, 20, RRA20PointCnt) //RRA20PointCnt=504: 20m1个点, 共504个点,即7d c.RRA("MAX", 0, 20, RRA20PointCnt) c.RRA("MIN", 0, 20, RRA20PointCnt) ....... return c.Create(true)}
能够应用rrdtool查问rrd文件的信息:其中保留了配置的归档策略
# rrdtool info be/be3cdc0fce0d498e19e2c6e9f1f338b6_GAUGE_60.rrdfilename = "be/be3cdc0fce0d498e19e2c6e9f1f338b6_GAUGE_60.rrd"rrd_version = "0003"step = 60last_update = 1591062120header_size = 3080ds[metric].index = 0ds[metric].type = "GAUGE"ds[metric].minimal_heartbeat = 120ds[metric].min = NaNds[metric].max = NaNds[metric].last_ds = "1"ds[metric].value = 0.0000000000e+00ds[metric].unknown_sec = 0rra[0].cf = "AVERAGE"rra[0].rows = 720rra[0].cur_row = 385rra[0].pdp_per_row = 1rra[0].xff = 0.0000000000e+00rra[0].cdp_prep[0].value = NaNrra[0].cdp_prep[0].unknown_datapoints = 0rra[1].cf = "AVERAGE"rra[1].rows = 576rra[1].cur_row = 543rra[1].pdp_per_row = 5rra[1].xff = 0.0000000000e+00rra[1].cdp_prep[0].value = 2.0000000000e+00rra[1].cdp_prep[0].unknown_datapoints = 0rra[2].cf = "MAX"rra[2].rows = 576rra[2].cur_row = 24rra[2].pdp_per_row = 5rra[2].xff = 0.0000000000e+00rra[2].cdp_prep[0].value = 1.0000000000e+00rra[2].cdp_prep[0].unknown_datapoints = 0rra[3].cf = "MIN".....
3. 应用rrd文件查问数据
先看下如何间接查问rrd文件中的指标数据,比方查问2020-06-02 09:30:46 ~ 2020-06-02 09:39:46这段时间, 每60s1个点的数据:
# rrdtool fetch be/be3cdc0fce0d498e19e2c6e9f1f338b6_GAUGE_60.rrd AVERAGE -r 60 -s 1591061446 -e 1591061986 metric1591061460: 1.0000000000e+001591061520: 1.0000000000e+001591061580: 1.0000000000e+001591061640: 1.0000000000e+001591061700: 1.0000000000e+001591061760: 1.0000000000e+001591061820: 1.0000000000e+001591061880: 1.0000000000e+001591061940: 1.0000000000e+001591062000: 1.0000000000e+00
再看一下代码中如何查问的:
- filename: 指标所在的文件;
- cf: 指标聚合办法,比方AVERAGE;
- start,end: 查问的起始/终止工夫;
- step: 指标的间隔时间;
// modules/graph/rrdtool/rrdtool.gofunc fetch(filename string, cf string, start, end int64, step int) ([]*cmodel.RRDData, error) { start_t := time.Unix(start, 0) end_t := time.Unix(end, 0) step_t := time.Duration(step) * time.Second fetchRes, err := rrdlite.Fetch(filename, cf, start_t, end_t, step_t) if err != nil { return []*cmodel.RRDData{}, err } defer fetchRes.FreeValues() values := fetchRes.Values() size := len(values) ret := make([]*cmodel.RRDData, size) start_ts := fetchRes.Start.Unix() step_s := fetchRes.Step.Seconds() for i, val := range values { ts := start_ts + int64(i+1)*int64(step_s) d := &cmodel.RRDData{ Timestamp: ts, Value: cmodel.JsonFloat(val), } ret[i] = d } return ret, nil}
4. 保留数据到rrd文件
通过rrdlite.Updater来实现,先将指标数据写入cache,而后一起update到rrd文件:
// modules/graph/rrdtool/rrdtool.gofunc update(filename string, items []*cmodel.GraphItem) error { u := rrdlite.NewUpdater(filename) for _, item := range items { v := math.Abs(item.Value) if v > 1e+300 || (v < 1e-300 && v > 0) { continue } if item.DsType == "DERIVE" || item.DsType == "COUNTER" { u.Cache(item.Timestamp, int(item.Value)) } else { u.Cache(item.Timestamp, item.Value) } } return u.Update()}