1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Prometheus時序數(shù)據(jù)庫-報警的計算

        共 5152字,需瀏覽 11分鐘

         ·

        2021-03-31 13:34

        Prometheus時序數(shù)據(jù)庫-報警的計算

        在前面的文章中,筆者詳細的闡述了Prometheus的數(shù)據(jù)插入存儲查詢等過程。但作為一個監(jiān)控神器,報警計算功能是必不可少的。自然的Prometheus也提供了靈活強大的報警規(guī)則可以讓我們自由去發(fā)揮。在本篇文章里,筆者就帶讀者去看下Prometheus內(nèi)部是怎么處理報警規(guī)則的。

        報警架構(gòu)

        Prometheus只負責(zé)進行報警計算,而具體的報警觸發(fā)則由AlertManager完成。如果我們不想改動AlertManager以完成自定義的路由規(guī)則,還可以通過webhook外接到另一個系統(tǒng)(例如,一個轉(zhuǎn)換到kafka的程序)。

        在本篇文章里,筆者并不會去設(shè)計alertManager,而是專注于Prometheus本身報警規(guī)則的計算邏輯。

        一個最簡單的報警規(guī)則

        rules:
        alert: HTTPRequestRateLow
        expr: http_requests < 100
        for: 60s
        labels:
        severity: warning
        annotations:
        description: "http request rate low"

        這上面的規(guī)則即是http請求數(shù)量<100持續(xù)1min,則我們開始報警,報警級別為warning

        什么時候觸發(fā)這個計算

        在加載完規(guī)則之后,Prometheus按照evaluation_interval這個全局配置去不停的計算Rules。代碼邏輯如下所示:

        rules/manager.go

        func (g *Group) run(ctx context.Context) {
        iter := func() {
        ......
        g.Eval(ctx,evalTimestamp)
        ......
        }
        // g.interval = evaluation_interval
        tick := time.NewTicker(g.interval)
        defer tick.Stop()
        ......
        for {
        ......
        case <-tick.C:
        ......
        iter()
        }
        }

        而g.Eval的調(diào)用為:

        func (g *Group) Eval(ctx context.Context, ts time.Time) {
        // 對所有的rule
        for i, rule := range g.rules {
        ......
        // 先計算出是否有符合rule的數(shù)據(jù)
        vector, err := rule.Eval(ctx, ts, g.opts.QueryFunc, g.opts.ExternalURL)
        ......
        // 然后發(fā)送
        ar.sendAlerts(ctx, ts, g.opts.ResendDelay, g.interval, g.opts.NotifyFunc)
        }
        ......
        }

        整個過程如下圖所示:

        對單個rule的計算

        我們可以看到,最重要的就是rule.Eval這個函數(shù)。代碼如下所示:

        func (r *AlertingRule) Eval(ctx context.Context, ts time.Time, query QueryFunc, externalURL *url.URL) (promql.Vector, error) {
        // 最終調(diào)用了NewInstantQuery
        res, err = query(ctx,r.vector.String(),ts)
        ......
        // 報警組裝邏輯
        ......
        // active 報警狀態(tài)變遷
        }

        這個Eval包含了報警的計算/組裝/發(fā)送的所有邏輯。我們先聚焦于最重要的計算邏輯。也就是其中的query。其實,這個query是對NewInstantQuery的一個簡單封裝。

        func EngineQueryFunc(engine *promql.Engine, q storage.Queryable) QueryFunc {
        return func(ctx context.Context, qs string, t time.Time) (promql.Vector, error) {
        q, err := engine.NewInstantQuery(q, qs, t)
        ......
        res := q.Exec(ctx)
        }
        }

        也就是說它執(zhí)行了一個瞬時向量的查詢。而其查詢的表達式按照我們之前給出的報警規(guī)則,即是

        http_requests < 100

        既然要計算表達式,那么第一步,肯定是將其構(gòu)造成一顆AST。其樹形結(jié)構(gòu)如下圖所示:

        解析出左節(jié)點是個VectorSelect而且知道了其lablelMatcher是

        __name__:http_requests

        那么我們就可以左節(jié)點VectorSelector進行求值。直接利用倒排索引在head中查詢即可(因為instant query的是當(dāng)前時間,所以肯定在內(nèi)存中)。

        想知道具體的計算流程,可以見筆者之前的博客《Prometheus時序數(shù)據(jù)庫-數(shù)據(jù)的查詢》
        計算出左節(jié)點的數(shù)據(jù)之后,我們就可以和右節(jié)點進行比較以計算出最終結(jié)果了。具體代碼為:

        func (ev *evaluator) eval(expr Expr) Value {
        ......
        case *BinaryExpr:
        ......
        case lt == ValueTypeVector && rt == ValueTypeScalar:
        return ev.rangeEval(func(v []Value, enh *EvalNodeHelper) Vector {
        return ev.VectorscalarBinop(e.Op, v[0].(Vector), Scalar{V: v[1].(Vector)[0].Point.V}, false, e.ReturnBool, enh)
        }, e.LHS, e.RHS)
        .......
        }

        最后調(diào)用的函數(shù)即為:

        func (ev *evaluator) VectorBinop(op ItemType, lhs, rhs Vector, matching *VectorMatching, returnBool bool, enh *EvalNodeHelper) Vector {
        // 對左節(jié)點計算出來的所有的數(shù)據(jù)sample
        for _, lhsSample := range lhs {
        ......
        // 由于左邊lv = 75 < 右邊rv = 100,且op為less
        /**
        vectorElemBinop(){
        case LESS
        return lhs, lhs < rhs
        }
        **/
        // 這邊得到的結(jié)果value=75,keep = true
        value, keep := vectorElemBinop(op, lv, rv)
        ......
        if keep {
        ......
        // 這邊就講75放到了輸出里面,也就是說我們最后的計算確實得到了數(shù)據(jù)。
        enh.out = append(enh.out.sample)
        }
        }
        }

        如下圖所示:

        最后我們的expr輸出即為

        sample {
        Point {t:0,V:75}
        Metric {__name__:http_requests,instance:0,job:api-server}

        }

        報警狀態(tài)變遷

        計算過程講完了,筆者還稍微講一下報警的狀態(tài)變遷,也就是最開始報警規(guī)則中的rule中的for,也即報警持續(xù)for(規(guī)則中為1min),我們才真正報警。為了實現(xiàn)這種功能,這就需要一個狀態(tài)機了。筆者這里只闡述下從Pending(報警出現(xiàn))->firing(真正發(fā)送)的邏輯。

        在之前的Eval方法里面,有下面這段

        func (r *AlertingRule) Eval(ctx context.Context, ts time.Time, query QueryFunc, externalURL *url.URL) (promql.Vector, error) {
        for _, smpl := range res {
        ......
        if alert, ok := r.active[h]; ok && alert.State != StateInactive {
        alert.Value = smpl.V
        alert.Annotations = annotations
        continue
        }
        // 如果這個告警不在active map里面,則將其放入
        // 注意,這里的hash依舊沒有拉鏈法,有極小概率hash沖突
        r.active[h] = &Alert{
        Labels: lbs,
        Annotations: annotations,
        ActiveAt: ts,
        State: StatePending,
        Value: smpl.V,
        }
        }
        ......
        // 報警狀態(tài)的變遷邏輯
        for fp, a := range r.active {
        // 如果當(dāng)前r.active的告警已經(jīng)不在剛剛計算的result里面了 if _, ok := resultFPs[fp]; !ok {
        // 如果狀態(tài)是Pending待發(fā)送
        if a.State == StatePending || (!a.ResolvedAt.IsZero() && ts.Sub(a.ResolvedAt) > resolvedRetention) {
        delete(r.active, fp)
        }
        ......
        continue
        }
        // 對于已有的Active報警,如果其Active的時間>r.holdDuration,也就是for指定的
        if a.State == StatePending && ts.Sub(a.ActiveAt) >= r.holdDuration {
        // 我們將報警置為需要發(fā)送
        a.State = StateFiring
        a.FiredAt = ts
        }
        ......

        }
        }

        上面代碼邏輯如下圖所示:

        總結(jié)

        Prometheus作為一個監(jiān)控神器,給我們提供了各種各樣的遍歷。其強大的報警計算功能就是其中之一。了解其中告警的計算原理,才能讓我們更好的運用它。


        瀏覽 63
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            99久久婷婷国产综合 | 蝌蚪成人网站www | 成人黄色视屏 | 欧美韩日在线 | 黄色漫画推荐 | 后入爆操 | 操逼免费电影 | 内射美女鲍免费视频 | 欧美日韩在线视频免费观看 | 女人被男人添荫蒂舒 |