Aura: 一个专注于监控和采集的 SDK 组件

chenjiandongx · · 227 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。
项目地址:https://github.com/chenjiandongx/aura ## ???? Overview ☁️ 在云原生时代,以 [Prometheus](https://prometheus.io) 为中心的监控生态已经逐渐完善,社区也出现了大量的中间件,数据库以及各种基础组件的 exporter,Prometheus 官方也给出了维护了一份 exporter 列表 [instrumenting/exporters](https://prometheus.io/docs/instrumenting/exporters)。 但是 Prometheus 的缺点和它的优点一样明显,缺少高可用的集群方案。想了解 Prometheus 和监控系统的同学可阅读 [Prometheus 折腾笔记](https://github.com/chenjiandongx/prometheus101) 系列文章。目前开源的高可用企业级的监控方案有两个,小米的 [falcon-plus](https://github.com/open-falcon/falcon-plus) 和滴滴出行的 [nightingale](https://github.com/didi/nightingale),后者是前者的优化增强版。国内的不少公司(比如我司 ????)的监控方案都或多或少参考了 falcon 的设计架构,falcon 的官网也维护了一份 [企业用户列表](http://book.open-falcon.org/zh_0_2/contributing.html)。 falcon 的设计架构决定了它强悍的性能及良好的可扩展性,具体关于其相关信息可参考 [官网介绍](http://book.open-falcon.org/zh_0_2/intro/)。falcon 的 slogan: > *open-falcon 的目标是做最开放、最好用的互联网企业级监控产品。* 目前 falcon 已经不再维护 ????,可能它已经完成了它的历史使命吧,提供一套完整监控系统的构建方案;不过 nightingale 接住了 falcon 手中的接力棒,为开源社区的监控领域又注入了新的活力 ????。虽然如此,但是 falcon/nightingale 所构建的生态也仍旧不完善,缺少像 Prometheus 生态的各类数据采集器(exporter)。以 Prometheus 为中心的采集器都是通过 **暴露 HTTP 端口** 来让服务端采集,是一种 **Pull** 模式,而 falcon 体系是采用 **Push** 模式,**客户端主动上报**。数据采集形态的不同应该是 Prometheus 和 falcon 的最大差异点。 ## ???? Idea ???? 如果有一种方案,能够以比较低的开发成本,将 Prometheus 的 exporter 转为换 falcon 的 collector,那样的话 falcon 的生态就会变得丰富多彩。 * Metric 是监控体系中的重要概念,一个 metric 代表着一个监控项。Java 有一个优秀的 metric 相关的开源库 [dropwizard/metrics](https://github.com/dropwizard/metrics),同时也有开发者基于该库开发了一个 Golang 版本 [rcrowley/go-metrics](https://github.com/rcrowley/go-metrics),关于这两个库的更多信息,可移步至项目其地址。 * Prometheus 本身在提供服务端的同时,也开发不同语言的 SDK 客户端,如 Golang 版本 [prometheus/client_golang](https://github.com/proemtheus/client_golang)。 当 rcrowley/go-metrics 遇上 prometheus/client_golang,[Aura](https://github.com/chenjiandongx/aura) 就出现啦 ????。如果你使用过 Prometheus 的 SDK,那你将会对 Aura 提供的接口非常熟悉。Aura 的目标是成为 falcon 体系的客户端 SDK。 ## ???? Installation ```shell $ go get -u github.com/chenjiandongx/aura/... ``` ## ???? Metric Aura 标准 Metric 结构,沿用了 falcon 的设计。 ```golang type Metric struct { Endpoint string Metric string Step uint32 Value interface{} Type ValueType Labels map[string]string Timestamp int64 } ``` ### * Counter Counter 单调递增,违反单调性时重置为 0。可以用于统计某些事件出现的次数,或者服务的 uptime。 ```golang type Counter interface { Collector Clear() Count() int64 Dec(int64) Inc(int64) } ``` ### * Gauge Gauge 记录瞬时值,可以用于记录系统当下时刻的状态,比如 CPU 使用率,使用内存大小,网络 IO 情况。 ```golang type Gauge interface { Collector Update(float64) Value() float64 } ``` ### * Histogram Histogram 主要用于表示一段时间范围内对数据进行采样,并能够对其指定区间以及总数进行统计,通常它采集的数据展示为直方图。 ```golang type Histogram interface { Collector Observe(int64) } ``` ### * Timer Timer 主要用于统计一段代码逻辑或一次事件的耗时分布。 ```golang type Timer interface { Collector Time(func()) Update(time.Duration) } ``` ## ???? Usage ### Registry Registry 负责注册和管理 Collectors 的生命周期。 ```golang // RegistryOpts 用于指定 Metrics 和 Desc channel 的缓存大小。 // 一般情况下不需要调整,如果采集指标量比较大的话,可以将 CapMetricChan 值设置大一点。 type RegistryOpts struct { CapMetricChan int // default 2500 CapDescChan int // default 20 } func NewRegistry(opts *RegistryOpts) *Registry ``` ### Collector 基本用法 ```golang package main import ( "time" "github.com/chenjiandongx/aura" "github.com/chenjiandongx/aura/reporter" "github.com/shirou/gopsutil/load" ) const ( namespace = "host" subsystem = "cpu" step = 10 ) var ( // 使用 aura.NewDesc 声明采集的指标 // NewDesc(fqName, help string, step uint32, labelKeys []string) *Desc // * fqName: 指标名称 // * help: 指标描述或者介绍(可为空) // * step: 指标步长 // * labelkeys: 指标 label keys。 cpuLoad1 = aura.NewDesc( aura.BuildFQName(namespace, subsystem, "loadavg.1"), "CPU load average over the last 1 minute", step, nil, ) cpuLoad5 = aura.NewDesc( aura.BuildFQName(namespace, subsystem, "loadavg.5"), "load average over the last 5 minute", step, nil, ) cpuLoad15 = aura.NewDesc( aura.BuildFQName(namespace, subsystem, "loadavg.15"), "load average over the last 15 minute", step, nil, ) ) type CPUCollector struct{} // Interval 实现了 aura.Collector 接口。声明采集时间。 func (c *CPUCollector) Interval() time.Duration { return 2 * time.Second } // Describe 实现了 aura.Collector 接口。注册指标。 func (c *CPUCollector) Describe(ch chan<- *aura.Desc) { ch <- cpuLoad1 ch <- cpuLoad5 ch <- cpuLoad15 } // Describe 实现了 aura.Collector 接口。指标具体采集逻辑。 func (c *CPUCollector) Collect(ch chan<- aura.Metric) { cpuLoad, _ := load.Avg() ch <- aura.MustNewConstMetric(cpuLoad1, aura.GaugeValue, cpuLoad.Load1) ch <- aura.MustNewConstMetric(cpuLoad5, aura.GaugeValue, cpuLoad.Load5) ch <- aura.MustNewConstMetric(cpuLoad15, aura.GaugeValue, cpuLoad.Load15) } func main() { // (1) 创建一个 Rigistry 对象 registry := aura.NewRegistry(nil) // (2) 注册 Collector registry.MustRegister(&CPUCollector{}) // (3) 注册 Reporter // reporter 负责将 metrics 输送到任意后端,开发者可自行为 registry 提供定制化后端 // reporter.DefaultStreamReporter 会将采集的指标输出到 stdout registry.AddReporter(reporter.DefaultStreamReporter) // 可选项:Serve 将会启动一个 HTTP 服务用于提供 collector 本身运行的信息。 go registry.Serve("127.0.0.1:9099") // (4) 开始采集指标 registry.Run() } ``` **运行结果** ```shell ~/project/golang/src/github.com/chenjiandongx/aura/examples/desc ???? go run . {Endpoint: Metric:host.cpu.loadavg.15 Step:10 Value:2.01318359375 Type:Gauge Labels:map[] Timestamp:1590776801} {Endpoint: Metric:host.cpu.loadavg.15 Step:10 Value:2.01318359375 Type:Gauge Labels:map[] Timestamp:1590776803} {Endpoint: Metric:host.cpu.loadavg.15 Step:10 Value:2.01318359375 Type:Gauge Labels:map[] Timestamp:1590776805} {Endpoint: Metric:host.cpu.loadavg.1 Step:10 Value:1.60791015625 Type:Gauge Labels:map[] Timestamp:1590776807} {Endpoint: Metric:host.cpu.loadavg.5 Step:10 Value:2.02587890625 Type:Gauge Labels:map[] Timestamp:1590776801} {Endpoint: Metric:host.cpu.loadavg.5 Step:10 Value:2.02587890625 Type:Gauge Labels:map[] Timestamp:1590776803} {Endpoint: Metric:host.cpu.loadavg.1 Step:10 Value:1.748046875 Type:Gauge Labels:map[] Timestamp:1590776805} {Endpoint: Metric:host.cpu.loadavg.15 Step:10 Value:2.0009765625 Type:Gauge Labels:map[] Timestamp:1590776807} ... ``` **Collector 指标及运行状态** ```shell ~/project/golang/src/github.com/chenjiandongx/aura ???? curl -s http://localhost:9099/-/metadata | jq [ { "metric": "host.cpu.loadavg.1", "help": "CPU load average over the last 1 minute", "step": 10 }, { "metric": "host.cpu.loadavg.5", "help": "load average over the last 5 minute", "step": 10 }, { "metric": "host.cpu.loadavg.15", "help": "load average over the last 15 minute", "step": 10 } ] ~/project/golang/src/github.com/chenjiandongx/aura ???? curl -s http://localhost:9099/-/stats | jq { "metricsChanCap": 2500, "metricsChanLen": 0 } ``` ### 客户端埋点形式 ```golang package main import ( "math/rand" "time" "github.com/chenjiandongx/aura" "github.com/chenjiandongx/aura/reporter" ) const ( step = 15 ) var ( // 声明采集指标 echo = aura.NewHistogramVec( "http.service", "simple echo service", step, 15*time.Second, []string{"endpoint", "uri", "status"}, // 直方图上报数据如果指定了 HVTypes/Percentiles 那上报就是计算后的指标 // 计算后的指标形式 // http.service.min // http.service.max // http.service.mean // http.service.count // http.service.0.50 // http.service.0.75 // http.service.0.90 // http.service.0.99 &aura.HistogramOpts{ HVTypes: []aura.HistogramVType{ aura.HistogramVTMin, aura.HistogramVTMax, aura.HistogramVTMean, aura.HistogramVTCount, }, Percentiles: []float64{0.5, 0.75, 0.9, 0.99}, }, ) ) func main() { registry := aura.NewRegistry(nil) registry.MustRegister(echo) go func() { for range time.Tick(200 * time.Millisecond) { echo.WithLabelValues("echo", "/api/index", "200").Observe(rand.Int63() % 600) echo.With(map[string]string{ "endpoint": "echo", "uri": "/api/noexists", "status": "404", }).Observe(rand.Int63() % 600) } }() registry.AddReporter(reporter.DefaultStreamReporter) go registry.Serve("localhost:9099") registry.Run() } ``` **运行结果** ```shell ~/project/golang/src/github.com/chenjiandongx/aura/examples/histogram ???? go run . {Endpoint:echo Metric:http.service.max Step:15 Value:590 Type:Gauge Labels:map[endpoint:echo status:200 uri:/api/index] Timestamp:1590778743} {Endpoint:echo Metric:http.service.0.75 Step:15 Value:460.5 Type:Gauge Labels:map[endpoint:echo status:200 uri:/api/index] Timestamp:1590778743} {Endpoint:echo Metric:http.service.0.99 Step:15 Value:590 Type:Gauge Labels:map[endpoint:echo status:200 uri:/api/index] Timestamp:1590778743} {Endpoint:echo Metric:http.service.count Step:15 Value:20 Type:Gauge Labels:map[endpoint:echo status:404 uri:/api/noexists] Timestamp:1590778743} {Endpoint:echo Metric:http.service.0.50 Step:15 Value:325.5 Type:Gauge Labels:map[endpoint:echo status:404 uri:/api/noexists] Timestamp:1590778743} {Endpoint:echo Metric:http.service.0.75 Step:15 Value:460.5 Type:Gauge Labels:map[endpoint:echo status:404 uri:/api/noexists] Timestamp:1590778743} {Endpoint:echo Metric:http.service.0.90 Step:15 Value:572.4000000000001 Type:Gauge Labels:map[endpoint:echo status:404 uri:/api/noexists] Timestamp:1590778743} {Endpoint:echo Metric:http.service.0.99 Step:15 Value:590 Type:Gauge Labels:map[endpoint:echo status:404 uri:/api/noexists] Timestamp:1590778743} ... ``` ### 自定义 Reporter ```golang package main import ( "os" "time" "github.com/chenjiandongx/aura" ) var ( // declare metrics uptime = aura.NewCounter( "service.uptime", "service uptime in seconds", 5, 5*time.Second, ) ) type MyReporter struct{} // Custom reporter which will writes data the local file. func (r MyReporter) Report(ch chan aura.Metric) { filename := "metrics.log" f, err := os.Create(filename) if err != nil { panic(err) } for m := range ch { if _, err := f.WriteString(m.String() + "\n"); err != nil { panic(err) } } } func main() { registry := aura.NewRegistry(nil) registry.MustRegister(uptime) go func() { for range time.Tick(1 * time.Second) { uptime.Inc(1) } }() registry.AddReporter(MyReporter{}) registry.Run() } ``` **运行结果** ```shell ~/project/golang/src/github.com/chenjiandongx/aura/examples/reporter ???? tail -f metrics.log <Metadata Endpoint:, Metric:service.uptime, Type:Counter Timestamp:1590945775, Step:5, Value:1, Tags:map[]> <Metadata Endpoint:, Metric:service.uptime, Type:Counter Timestamp:1590945778, Step:5, Value:5, Tags:map[]> <Metadata Endpoint:, Metric:service.uptime, Type:Counter Timestamp:1590945783, Step:5, Value:10, Tags:map[]> <Metadata Endpoint:, Metric:service.uptime, Type:Counter Timestamp:1590945788, Step:5, Value:15, Tags:map[]> <Metadata Endpoint:, Metric:service.uptime, Type:Counter Timestamp:1590945793, Step:5, Value:19, Tags:map[]> <Metadata Endpoint:, Metric:service.uptime, Type:Counter Timestamp:1590945798, Step:5, Value:25, Tags:map[]> <Metadata Endpoint:, Metric:service.uptime, Type:Counter Timestamp:1590945803, Step:5, Value:29, Tags:map[]> <Metadata Endpoint:, Metric:service.uptime, Type:Counter Timestamp:1590945808, Step:5, Value:34, Tags:map[]> ... ``` Aura 提供了一些示例位于 [examples](https://github.com/chenjiandongx/aura/tree/master/examples) 文件夹。同时也基于 [prometheus/memcached_exporter](https://github.com/prometheus/memcached_exporter) 开发了 [memcached-collector](https://github.com/chenjiandongx/memcached-collector),作为一个标准 collector 写法供使用的同学参考。

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:701969077

227 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传