使用Prometheus

动机

最近需要将流程的耗时接入可观测系统,方便后续优化性能。为了统一技术栈,决定使用prometheus来接入。特别的是,我需要使用自定义的时间戳,由于PushGateway不支持自定义的时间戳,只能使用 pull 的方式了。

整体流程

1、构建 Collector
2、注册到 Register(也实现了 Gatherer 接口),其中会调用 CollectorDescribe方法。
3、通过http请求到 /metrics,使用 promhttp.Handler 来处理请求。
4、调用 RegisterGather方法,其中会调用 注册的CollectorCollect方法。
5、将 Metric 按名称分组为 dto.MetricFamily,编码后写入 ResponseWriter,返回给请求端。

实现细节

实现 Collector

type timestampedCollector struct {
    bufferChan chan prometheus.Metric
    desc       *prometheus.Desc
}

func newTimestampedCollector(bufferSize int, desc *prometheus.Desc) *timestampedCollector {
    metrics := make(chan prometheus.Metric, bufferSize)
    return &timestampedCollector{
        bufferChan: metrics,
        desc:       desc,
    }
}

func (t *timestampedCollector) add(m prometheus.Metric) {
    logger.Infof("add metric:%s", util.ToJson(m.Desc()))
    t.bufferChan <- m
}

func (t *timestampedCollector) Describe(c chan<- *prometheus.Desc) {
    c <- t.desc
}

func (t *timestampedCollector) Collect(c chan<- prometheus.Metric) {
    for {
        select {
        case m := <-t.bufferChan:
            c <- m
        case <-time.After(time.Second * 1):
            return
        }
    }
}

prometheus中有2种Collector,一种是uncheckedCollectors,还有一种是checkedCollectors,就是看你实现的CollectorDescribe方法中有没有添加prometheus.Desc

注册到Registry

构建prometheus.Desc

var totalDesc = prometheus.NewDesc("emr_exclude_bootstrap_duration_seconds",
    "流程去掉执行引导操作的时间",
    []string{"service", "region", "nodeCnt", "flowId"},
    nil)

NewDesc生成 prometheus.Desc的id,总的来说就是(fqName + constLabels的值列表) 合并起来的string的hash值

func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, constLabels Labels) *Desc {
        // 构建 Desc
        ......
        labelValues := make([]string, 1, len(constLabels)+1)
    labelValues[0] = fqName
        for _, labelName := range labelNames {
        labelValues = append(labelValues, constLabels[labelName])
    }
        xxh := xxhash.New()
    for _, val := range labelValues {
        xxh.WriteString(val)
        xxh.Write(separatorByteSlice)
    }
    d.id = xxh.Sum64() // 生成 Desc 的 id
}

NewDesc生成 prometheus.Desc的dimHash,总的来说就是(fqName + constLabels的值列表) 合并起来的string的hash值

func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, constLabels Labels) *Desc {
        // 构建 Desc
        ......
        labelNames := make([]string, 0, len(constLabels)+len(d.variableLabels.names))
        for labelName := range constLabels {
        labelNames = append(labelNames, labelName)
    }
      for _, label := range variableLabels {
        labelNames = append(labelNames, "$"+label)
    }
        xxh := xxhash.New()
    xxh.WriteString(help)
    xxh.Write(separatorByteSlice)
    for _, labelName := range labelNames {
        xxh.WriteString(labelName)
        xxh.Write(separatorByteSlice)
    }
    d.dimHash = xxh.Sum64()
}

注册到prometheus

totalCollector = newTimestampedCollector(1000, totalDesc)
if err := handleErr(prometheus.Register(totalCollector)); err != nil {
    panic(err)
}

伪代码如下

func (r *Registry) Register(c Collector) error {
        c.Describe(descChan)
        close(descChan)
        for desc := range descChan {
            1、desc.id不能重复
            2、desc.dimHash + desc.fqName 不能重复
        }
}

使用 promhttp.Handler 来处理请求

func main() {
    http.Handle("/metrics", promhttp.Handler())
        // 暴露自己的指标
    http.ListenAndServe(":13000", nil)
}

使用curl -XGET 'http://localhost:13000/metrics'来验证metric是否暴露成功。其中 http handler的链表结构为:

InstrumentHandlerCounter -- HandlerForTransactional内部方法

暴露指标

promhttp.Handler最核心的就是调用 RegistryGather方法,其中会调用注册的CollectorCollect方法来将指标拉取出来,接着主要是2步
1、聚合

MetricFamily 按照Metric fqName分组,相同Metric fqNameMetric会放入到 MetricFamily 的集合中

2、校验

1、指标名称 + lable的名字 + label的值 + 时间戳 不能重复
2、label不能重复

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348

推荐阅读更多精彩内容