Golang框架實戰(zhàn)-KisFlow流式計算框架(11)-Prometheus Metrics統(tǒng)計

Golang框架實戰(zhàn)-KisFlow流式計算框架專欄

Golang框架實戰(zhàn)-KisFlow流式計算框架(1)-概述
Golang框架實戰(zhàn)-KisFlow流式計算框架(2)-項目構(gòu)建/基礎(chǔ)模塊-(上)
Golang框架實戰(zhàn)-KisFlow流式計算框架(3)-項目構(gòu)建/基礎(chǔ)模塊-(下)
Golang框架實戰(zhàn)-KisFlow流式計算框架(4)-數(shù)據(jù)流
Golang框架實戰(zhàn)-KisFlow流式計算框架(5)-Function調(diào)度
Golang框架實戰(zhàn)-KisFlow流式計算框架(6)-Connector
Golang框架實戰(zhàn)-KisFlow流式計算框架(7)-配置導(dǎo)入與導(dǎo)出
Golang框架實戰(zhàn)-KisFlow流式計算框架(8)-KisFlow Action
Golang框架實戰(zhàn)-KisFlow流式計算框架(9)-Cache/Params 數(shù)據(jù)緩存與數(shù)據(jù)參數(shù)
Golang框架實戰(zhàn)-KisFlow流式計算框架(10)-Flow多副本


在介紹本章之前,我們先普及一下Prometheus Metrics的服務(wù)啟動辦法。 有關(guān)Prometheus是個什么東東,希望大家可以去額外補充下知識,我用一句大白話來解釋就是,系統(tǒng)的監(jiān)控指標(biāo)。

那么KisFlow作為流式計算框架,那么有關(guān)每個Function的調(diào)度時間、總體的數(shù)據(jù)量、算法速度等等指標(biāo)可能也是項目中或者開發(fā)者所要關(guān)注的一些數(shù)據(jù),那么這些數(shù)據(jù),經(jīng)過KisFlow,可以通過Prometheus的Metrics打點來進行記錄。

所以接下來我們可以通過全局的配置,開發(fā)者可以選擇是否開啟Prometheus的數(shù)據(jù)埋點能力。

10.1 Prometheus Metrics服務(wù)

10.1.1 prometheus client sdk

首先在kis-flow/go.mod中,新增require:

module kis-flow

go 1.18

require (
    github.com/google/uuid v1.5.0
    github.com/patrickmn/go-cache v2.1.0+incompatible
    github.com/prometheus/client_golang v1.14.0  //++++++++
    gopkg.in/yaml.v3 v3.0.1
)

這里我們采用prometheus的官方Golang客戶端SDK。https://github.com/prometheus/client_golang 有關(guān)具體的介紹,參考官方的README文檔:https://github.com/prometheus/client_golang/blob/main/README.md

其次,我們先簡單的寫一個prometheus的服務(wù),可以讓外界得到KisFlow服務(wù)的相關(guān)指標(biāo)數(shù)據(jù)等。 在kis-flow/下新建kis-flow/metrics/目錄,作為KisFlow統(tǒng)計指標(biāo)的代碼部分。

kis-flow/metrics/kis_metrics.go

package metrics

import (
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "kis-flow/common"
    "kis-flow/log"
    "net/http"
)


// RunMetricsService 啟動Prometheus監(jiān)控服務(wù)
func RunMetricsService(serverAddr string) error {

    // 注冊Prometheus 監(jiān)控路由路徑
    http.Handle(common.METRICS_ROUTE, promhttp.Handler())

    // 啟動HttpServer
    err := http.ListenAndServe(serverAddr, nil) //多個進程不可監(jiān)聽同一個端口
    if err != nil {
        log.Logger().ErrorF("RunMetricsService err = %s\n", err)
    }

    return err
}

其中METRICS_ROUTE作為監(jiān)控服務(wù)的http路由路徑,定義在kis-flow/common/const.go中:
如下:

kis-flow/common/const.go

// ... ...

// metrics
const (
    METRICS_ROUTE string = "/metrics"
)

// ... ...

接下來來簡單說明下上述的代碼,RunMetricsService() 是啟動prometheus監(jiān)控的http服務(wù)代碼,為什么要啟動這個服務(wù),目的是,我們可以通過http的請求來獲取kisflow目前進程的運行指標(biāo),那么都有哪些指標(biāo),現(xiàn)在我們還沒有進行統(tǒng)計,prometheus會默認提供當(dāng)前進程的go版本號、gc垃圾回收時間、內(nèi)存分配等等基礎(chǔ)統(tǒng)計指標(biāo)信息。

  • serverAddr參數(shù): 這個作為prometheus監(jiān)控的地址,一般是本地地址加上一個端口號:如"0.0.0.0:20004"。
http.Handle(common.METRICS_ROUTE, promhttp.Handler())

這行代碼則表示,"0.0.0.0:20004/metrics" 為獲取指標(biāo)入口。

上述代碼寫完之后,別忘了拉去一下https://github.com/prometheus/client_golang 相關(guān)的依賴包。

$ go mod tidy

拉取之后,當(dāng)前的go.mod 的依賴大致如下(會有版本號的區(qū)別):

kis-flow/go.mod

module kis-flow

go 1.18

require (
    github.com/google/uuid v1.5.0
    github.com/patrickmn/go-cache v2.1.0+incompatible
    github.com/prometheus/client_golang v1.14.0
    gopkg.in/yaml.v3 v3.0.1
)

require (
    github.com/beorn7/perks v1.0.1 // indirect
    github.com/cespare/xxhash/v2 v2.1.2 // indirect
    github.com/golang/protobuf v1.5.2 // indirect
    github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
    github.com/prometheus/client_model v0.3.0 // indirect
    github.com/prometheus/common v0.37.0 // indirect
    github.com/prometheus/procfs v0.8.0 // indirect
    golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
    google.golang.org/protobuf v1.28.1 // indirect
)

10.1.2 prometheus server 服務(wù)啟動單元測試

接下來來簡單測試下服務(wù)是否可以啟動。

kis-flow/test/下創(chuàng)建prometheus_server_test.go文件:

kis-flow/test/prometheus_server_test.go

package test

import (
    "kis-flow/metrics"
    "testing"
)

func TestPrometheusServer(t *testing.T) {

    err := metrics.RunMetricsService("0.0.0.0:20004")
    if err != nil {
        panic(err)
    }
}

這里的監(jiān)控地址為"0.0.0.0:20004"。接下來來啟動本單元測試用例,打開一個終端A,cdkis-flow/test/目錄下:

 $ go test -test.v -test.paniconexit0 -test.run TestPrometheusServer
 === RUN   TestPrometheusServer
 

然后打開另一個終端B,輸入如下指令,模擬http客戶端進行請求:

 $ curl http://0.0.0.0:20004/metrics

之后我們在終端B得到監(jiān)控指標(biāo)的結(jié)果如下:

# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
go_gc_duration_seconds_count 0
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 8
# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.18.8"} 1
# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
# TYPE go_memstats_alloc_bytes gauge
go_memstats_alloc_bytes 3.2364e+06
# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.
# TYPE go_memstats_alloc_bytes_total counter
go_memstats_alloc_bytes_total 3.2364e+06
# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table.
# TYPE go_memstats_buck_hash_sys_bytes gauge
go_memstats_buck_hash_sys_bytes 1.446507e+06
# HELP go_memstats_frees_total Total number of frees.
# TYPE go_memstats_frees_total counter
go_memstats_frees_total 0
# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
# TYPE go_memstats_gc_sys_bytes gauge
go_memstats_gc_sys_bytes 3.561224e+06
# HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use.
# TYPE go_memstats_heap_alloc_bytes gauge
go_memstats_heap_alloc_bytes 3.2364e+06
# HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used.
# TYPE go_memstats_heap_idle_bytes gauge
go_memstats_heap_idle_bytes 4.636672e+06
# HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use.
# TYPE go_memstats_heap_inuse_bytes gauge
go_memstats_heap_inuse_bytes 3.260416e+06
# HELP go_memstats_heap_objects Number of allocated objects.
# TYPE go_memstats_heap_objects gauge
go_memstats_heap_objects 21294
# HELP go_memstats_heap_released_bytes Number of heap bytes released to OS.
# TYPE go_memstats_heap_released_bytes gauge
go_memstats_heap_released_bytes 4.636672e+06
# HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system.
# TYPE go_memstats_heap_sys_bytes gauge
go_memstats_heap_sys_bytes 7.897088e+06
# HELP go_memstats_last_gc_time_seconds Number of seconds since 1970 of last garbage collection.
# TYPE go_memstats_last_gc_time_seconds gauge
go_memstats_last_gc_time_seconds 0
# HELP go_memstats_lookups_total Total number of pointer lookups.
# TYPE go_memstats_lookups_total counter
go_memstats_lookups_total 0
# HELP go_memstats_mallocs_total Total number of mallocs.
# TYPE go_memstats_mallocs_total counter
go_memstats_mallocs_total 21294
# HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures.
# TYPE go_memstats_mcache_inuse_bytes gauge
go_memstats_mcache_inuse_bytes 9600
# HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system.
# TYPE go_memstats_mcache_sys_bytes gauge
go_memstats_mcache_sys_bytes 15600
# HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures.
# TYPE go_memstats_mspan_inuse_bytes gauge
go_memstats_mspan_inuse_bytes 46376
# HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system.
# TYPE go_memstats_mspan_sys_bytes gauge
go_memstats_mspan_sys_bytes 48960
# HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place.
# TYPE go_memstats_next_gc_bytes gauge
go_memstats_next_gc_bytes 4.194304e+06
# HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations.
# TYPE go_memstats_other_sys_bytes gauge
go_memstats_other_sys_bytes 1.171301e+06
# HELP go_memstats_stack_inuse_bytes Number of bytes in use by the stack allocator.
# TYPE go_memstats_stack_inuse_bytes gauge
go_memstats_stack_inuse_bytes 491520
# HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator.
# TYPE go_memstats_stack_sys_bytes gauge
go_memstats_stack_sys_bytes 491520
# HELP go_memstats_sys_bytes Number of bytes obtained from system.
# TYPE go_memstats_sys_bytes gauge
go_memstats_sys_bytes 1.46322e+07
# HELP go_threads Number of OS threads created.
# TYPE go_threads gauge
go_threads 7
# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served.
# TYPE promhttp_metric_handler_requests_in_flight gauge
promhttp_metric_handler_requests_in_flight 1
# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code.
# TYPE promhttp_metric_handler_requests_total counter
promhttp_metric_handler_requests_total{code="200"} 1
promhttp_metric_handler_requests_total{code="500"} 0
promhttp_metric_handler_requests_total{code="503"} 0

我們已經(jīng)給KisFlow提供了Function,Flow,Connector等的配置是通過kistype來區(qū)分的。
接下來我們來實現(xiàn)kistype等于global全局配置,在這個配置里,我們來設(shè)置是否啟動Prometheus和Metrics統(tǒng)計的開關(guān)。
接下來給KisFlow加上全局配置文件的屬性加載。

10.2 KisFlow全局配置

10.2.1 全局配置文件加載

全局配置的yaml的格式如下:

#kistype Global為kisflow的全局配置
kistype: global
#是否啟動prometheus監(jiān)控
prometheus_enable: true
#是否需要kisflow單獨啟動端口監(jiān)聽
prometheus_listen: true
#prometheus取點監(jiān)聽地址
prometheus_serve: 0.0.0.0:20004

10.2.2 結(jié)構(gòu)體定義

接下來我們根據(jù)上述的配置協(xié)議,來定義KisFlow的策略配置結(jié)構(gòu)體,并且提供一些響應(yīng)的初始化方法。
我們在項目文檔中創(chuàng)建kis_global_config.go文件,在這里我們將需要的Config定義實現(xiàn)。

kis-flow/config/kis_global_config.go

package config

type KisGlobalConfig struct {
    //kistype Global為kisflow的全局配置
    KisType string `yaml:"kistype"`
    //是否啟動prometheus監(jiān)控
    EnableProm bool `yaml:"prometheus_enable"`
    //是否需要kisflow單獨啟動端口監(jiān)聽
    PrometheusListen bool `yaml:"prometheus_listen"`
    //prometheus取點監(jiān)聽地址
    PrometheusServe string `yaml:"prometheus_serve"`
}

// GlobalConfig 默認全局配置,全部均為關(guān)閉
var GlobalConfig = new(KisGlobalConfig)

這里提供了一個全局的GlobalConfig對象,并且是公有變量,方便其他模塊共享全局配置。

10.2.3 配置文件解析

接下來,針對全局配置做做導(dǎo)入配置的解析,在kif-flow/flie/config_import.go中,添加如下函數(shù):

kis-flow/file/config_import.go

// kisTypeGlobalConfigure 解析Global配置文件,yaml格式
func kisTypeGlobalConfigure(confData []byte, fileName string, kisType interface{}) error {
    // 全局配置
    if ok := yaml.Unmarshal(confData, config.GlobalConfig); ok != nil {
        return errors.New(fmt.Sprintf("%s is wrong format kisType = %s", fileName, kisType))
    }

    // TODO 初始化Prometheus指標(biāo)


    // TODO 啟動Prometheus指標(biāo)Metrics服務(wù)

    return nil
}

這里加載全局的yaml配置文件,加載之后,判斷是否要啟動初始化Prometheus指標(biāo)監(jiān)控,這個稍后我們再添加。

那么kisTypeGlobalConfigure()在哪里被調(diào)度,和其他的配置文件一樣,在加載掃描本地配置文件的時候,被調(diào)度即可,如下:

kis-flow/file/config_import.go

// parseConfigWalkYaml 全盤解析配置文件,yaml格式, 講配置信息解析到allConfig中
func parseConfigWalkYaml(loadPath string) (*allConfig, error) {
    // ... ...

    err := filepath.Walk(loadPath, func(filePath string, info os.FileInfo, err error) error {
        // ... ... 

        // 判斷kisType是否存在
        if kisType, ok := confMap["kistype"]; !ok {
            return errors.New(fmt.Sprintf("yaml file %s has no file [kistype]!", filePath))
        } else {
            switch kisType {
            case common.KisIdTypeFlow:
                return kisTypeFlowConfigure(all, confData, filePath, kisType)

            case common.KisIdTypeFunction:
                return kisTypeFuncConfigure(all, confData, filePath, kisType)

            case common.KisIdTypeConnnector:
                return kisTypeConnConfigure(all, confData, filePath, kisType)

            // +++++++++++++++++++++++++++++++++
            case common.KisIdTypeGlobal:
                return kisTypeGlobalConfigure(confData, filePath, kisType)
            // +++++++++++++++++++++++++++++++++

            default:
                return errors.New(fmt.Sprintf("%s set wrong kistype %s", filePath, kisType))
            }
        }
    })

    if err != nil {
        return nil, err
    }

    return all, nil
}

在這里,我們增加kistype的Case:KisIdTypeGlobal來調(diào)用kisTypeGlobalConfigure()

接下來,我們來創(chuàng)建Mertrics統(tǒng)計模塊,本節(jié)先統(tǒng)計一個最簡單的指標(biāo),KisFlow當(dāng)前處理過的數(shù)據(jù)總量(以處理的源數(shù)據(jù)數(shù)量為準(zhǔn))

10.3 Metrics統(tǒng)計指標(biāo)-DataTotal全量數(shù)據(jù)

10.3.1 KisMertics

首先創(chuàng)建一個KisMrtics模塊,創(chuàng)建目錄kis-flow/metrics/,并且創(chuàng)建文件kis_metrics.go文件:

kis-flow/metrics/kis_metrics.go

package metrics

import (
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "kis-flow/common"
    "kis-flow/log"
    "net/http"
)

// kisMetrics kisFlow的Prometheus監(jiān)控指標(biāo)
type kisMetrics struct {
    //數(shù)據(jù)數(shù)量總量
    DataTotal prometheus.Counter
}

var Metrics *kisMetrics

// RunMetricsService 啟動Prometheus監(jiān)控服務(wù)
func RunMetricsService(serverAddr string) error {

    // 注冊Prometheus 監(jiān)控路由路徑
    http.Handle(common.METRICS_ROUTE, promhttp.Handler())

    // 啟動HttpServer
    err := http.ListenAndServe(serverAddr, nil) //多個進程不可監(jiān)聽同一個端口
    if err != nil {
        log.Logger().ErrorF("RunMetricsService err = %s\n", err)
    }

    return err
}

func InitMetrics() {
    Metrics = new(kisMetrics)

    // DataTotal初始化Counter
    Metrics.DataTotal = prometheus.NewCounter(prometheus.CounterOpts{
        Name: common.COUNTER_KISFLOW_DATA_TOTAL_NAME,
        Help: common.COUNTER_KISFLOW_DATA_TOTAL_HELP,
    })

    // 注冊Metrics
    prometheus.MustRegister(Metrics.DataTotal)
}

  • kisMetrics struct: 為KisFlow的需要統(tǒng)計的指標(biāo),目前只有一個統(tǒng)計指標(biāo)DataTotal,類型為prometheus.Counter(有關(guān)prometheus.Counter類型的說明請參考:有關(guān)prometheus.Counter指標(biāo)的概念)
  • Metrics *kisMetrics:是KisFlow一個全局的指標(biāo)統(tǒng)計對象,公有,方便其他模塊獲取。
  • RunMetricsService(serverAddr string): 為啟動prometheus服務(wù)監(jiān)聽,在之前的章節(jié)我們已經(jīng)對這部分做了單元測試。
  • InitMetrics(): 為初始化全局對象和一些指標(biāo)的初始化,最后需要調(diào)用prometheus.MustRegister將指標(biāo)注冊到prometheus中,這是prometheus統(tǒng)計編程的必要基本過程。

這里面有兩個常量,分別表示指標(biāo)顯示名稱和含義,我們定義在下面:

kis-flow/common/const.go

// metrics
const (
    METRICS_ROUTE string = "/metrics"

    COUNTER_KISFLOW_DATA_TOTAL_NAME string = "kisflow_data_total"
    COUNTER_KISFLOW_DATA_TOTAL_HELP string = "KisFlow全部Flow的數(shù)據(jù)總量"
)

10.3.2 DataTotal指標(biāo)統(tǒng)計

作為KisFlow的處理全量數(shù)據(jù),我們只需要在commitSrcData ()方法中統(tǒng)計就可以了,commitSrcData() 提交當(dāng)前Flow的數(shù)據(jù)源數(shù)據(jù), 表示首次提交當(dāng)前Flow的原始數(shù)據(jù)源 ,表示數(shù)據(jù)首次進入KisFlow中,下面我們添加代碼如下:

kis-flow/flow/kis_flow_data.go

func (flow *KisFlow) commitSrcData(ctx context.Context) error {

    // 制作批量數(shù)據(jù)batch
    dataCnt := len(flow.buffer)
    batch := make(common.KisRowArr, 0, dataCnt)

    for _, row := range flow.buffer {
        batch = append(batch, row)
    }

    // 清空之前所有數(shù)據(jù)
    flow.clearData(flow.data)

    // 首次提交,記錄flow原始數(shù)據(jù)
    // 因為首次提交,所以PrevFunctionId為FirstVirtual 因為沒有上一層Function
    flow.data[common.FunctionIdFirstVirtual] = batch

    // 清空緩沖Buf
    flow.buffer = flow.buffer[0:0]

    // +++++++++++++++++++++++++++++++
    // 首次提交數(shù)據(jù)源數(shù)據(jù),進行統(tǒng)計數(shù)據(jù)總量
    if config.GlobalConfig.EnableProm == true {
        // 統(tǒng)計數(shù)據(jù)總量 Metrics.DataTota 指標(biāo)累計加1
        metrics.Metrics.DataTotal.Add(float64(dataCnt))
    }
    // ++++++++++++++++++++++++++++++

    log.Logger().DebugFX(ctx, "====> After CommitSrcData, flow_name = %s, flow_id = %s\nAll Level Data =\n %+v\n", flow.Name, flow.Id, flow.data)

    return nil
}

先根據(jù)全局配置判斷是否統(tǒng)計指標(biāo),如果為true則,通過下面代碼來對metrics的全量數(shù)據(jù)進行統(tǒng)計:

        metrics.Metrics.DataTotal.Add(float64(dataCnt))

dataCnt為累計增加的數(shù)量。

10.3.3 Metrics啟動

在導(dǎo)入配置之后,我們需要啟動metrics服務(wù),調(diào)度如下:

kis-flow/file/config_import.go

// kisTypeGlobalConfigure 解析Global配置文件,yaml格式
func kisTypeGlobalConfigure(confData []byte, fileName string, kisType interface{}) error {
    // 全局配置
    if ok := yaml.Unmarshal(confData, config.GlobalConfig); ok != nil {
        return errors.New(fmt.Sprintf("%s is wrong format kisType = %s", fileName, kisType))
    }

    // ++++++++++++++++++++
    // 啟動Metrics服務(wù)
    metrics.RunMetrics()

    return nil
}

其中RunMetrics()是實現(xiàn)如下:

kis-flow/metrics/kis_metrics.go

// RunMetrics 啟動Prometheus指標(biāo)服務(wù)
func RunMetrics() {
    // 初始化Prometheus指標(biāo)
    InitMetrics()

    if config.GlobalConfig.EnableProm == true && config.GlobalConfig.PrometheusListen == true {
        // 啟動Prometheus指標(biāo)Metrics服務(wù)
        go RunMetricsService(config.GlobalConfig.PrometheusServe)
    }
}

這樣,在導(dǎo)入全局配置后,看是否開啟統(tǒng)計,如果統(tǒng)計,我們就會開一個協(xié)程來啟動PrometheusServe,監(jiān)聽的ip和端口會在配置文件里進行配置。

接下來我們先對DataTotal指標(biāo)做一個單元測試,來進行驗證。

10.4 KisMetrics單元測試

10.4.1 全局配置文件創(chuàng)建

創(chuàng)建一個全局配置文件kis-flow.ymlkis-flow/test/load_conf/下,內(nèi)容如下:

kis-flow/test/load_conf/kis-flow.yml

#kistype Global為kisflow的全局配置
kistype: global
#是否啟動prometheus監(jiān)控
prometheus_enable: true
#是否需要kisflow單獨啟動端口監(jiān)聽
prometheus_listen: true
#prometheus取點監(jiān)聽地址
prometheus_serve: 0.0.0.0:20004

10.4.2 新建單元測試

接下來創(chuàng)建測試用例代碼,在kis-flow/test/下,創(chuàng)建kis_metrics_test.go文件,如下:

kis-flow/test/kis_metrics_test.go

package test

import (
    "context"
    "kis-flow/common"
    "kis-flow/file"
    "kis-flow/kis"
    "kis-flow/test/caas"
    "kis-flow/test/faas"
    "testing"
    "time"
)

func TestMetricsDataTotal(t *testing.T) {
    ctx := context.Background()

    // 0. 注冊Function 回調(diào)業(yè)務(wù)
    kis.Pool().FaaS("funcName1", faas.FuncDemo1Handler)
    kis.Pool().FaaS("funcName2", faas.FuncDemo2Handler)
    kis.Pool().FaaS("funcName3", faas.FuncDemo3Handler)

    // 0. 注冊ConnectorInit 和 Connector 回調(diào)業(yè)務(wù)
    kis.Pool().CaaSInit("ConnName1", caas.InitConnDemo1)
    kis.Pool().CaaS("ConnName1", "funcName2", common.S, caas.CaasDemoHanler1)

    // 1. 加載配置文件并構(gòu)建Flow
    if err := file.ConfigImportYaml("/Users/tal/gopath/src/kis-flow/test/load_conf/"); err != nil {
        panic(err)
    }

    // 2. 獲取Flow
    flow1 := kis.Pool().GetFlow("flowName1")
    
    n := 0

    for n < 10 {
        // 3. 提交原始數(shù)據(jù)
        _ = flow1.CommitRow("This is Data1 from Test")

        // 4. 執(zhí)行flow1
        if err := flow1.Run(ctx); err != nil {
            panic(err)
        }

        time.Sleep(1 * time.Second)
        n++
    }
    
    select {}
}

這個Case和我們一般啟動KisFlow一樣,只不過,這里面會出現(xiàn)一個for循環(huán),每割1秒回啟動一次流式計算,并且提交一條數(shù)據(jù),一共循環(huán)10次,之后我們可以通過prometheus的監(jiān)聽服務(wù)來查看數(shù)據(jù)的總量。
最后加select{}的目的是為了防止主協(xié)程退出,導(dǎo)致prometheus的監(jiān)聽子協(xié)程連同退出。

執(zhí)行單元測試,cd到kis-flow/test/下,執(zhí)行:

go test -test.v -test.paniconexit0 -test.run TestMetricsDataTotal

會得到很多日志輸出,我們等待10s,之后再開啟一個終端,輸入如下指令:

 $ curl http://0.0.0.0:20004/metrics 

得到如下結(jié)果:

# ... ...
# HELP kisflow_data_total KisFlow全部Flow的數(shù)據(jù)總量
# TYPE kisflow_data_total counter
kisflow_data_total 10
# ... ...

其中我們會發(fā)現(xiàn),kisflow_data_total指標(biāo)已經(jīng)出現(xiàn),結(jié)果是10,說明我們的metrics指標(biāo)已經(jīng)可以統(tǒng)計了,那么接下來我們就可以再基于這個邏輯,新增一些其他KisFlow需要關(guān)心的比較復(fù)雜的指標(biāo)。

接下來我們來統(tǒng)計其他的一些關(guān)鍵指標(biāo),包括:Flow處理數(shù)據(jù)總量、Flow被調(diào)度次數(shù)、Function被調(diào)度次數(shù)、Flow執(zhí)行時間、Function執(zhí)行時間等。

10.5 Metrics統(tǒng)計指標(biāo)-其他統(tǒng)計指標(biāo)

10.5.1 指標(biāo):Flow處理數(shù)據(jù)總量

(1)指標(biāo)定義

首先定義指標(biāo)類型,如下:

kis-flow/metrics/kis_metrics.go

// kisMetrics kisFlow的Prometheus監(jiān)控指標(biāo)
type kisMetrics struct {
    // 數(shù)據(jù)數(shù)量總量
    DataTotal prometheus.Counter
    // 各Flow處理數(shù)據(jù)總量
    FlowDataTotal *prometheus.GaugeVec
}

FlowDataTotal 采用 prometheus.GaugeVec類型。主要是為了區(qū)分是哪個Flow產(chǎn)生的數(shù)據(jù)。

(2)指標(biāo)初始化及注冊

kis-flow/metrics/kis_metrics.go

func InitMetrics() {
    Metrics = new(kisMetrics)

    // DataTotal初始化Counter
    Metrics.DataTotal = prometheus.NewCounter(prometheus.CounterOpts{
        Name: common.COUNTER_KISFLOW_DATA_TOTAL_NAME,
        Help: common.COUNTER_KISFLOW_DATA_TOTAL_HELP,
    })

    // +++++++++++
    // FlowDataTotal初始化GaugeVec
    Metrics.FlowDataTotal = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: common.GANGE_FLOW_DATA_TOTAL_NAME,
            Help: common.GANGE_FLOW_DATA_TOTAL_HELP,
        },
        // 標(biāo)簽名稱
        []string{common.LABEL_FLOW_NAME},
    )

    // 注冊Metrics
    prometheus.MustRegister(Metrics.DataTotal)
    prometheus.MustRegister(Metrics.FlowDataTotal) // +++
}

相關(guān)常量定義:

kis-flow/common/const.go


// metrics
const (
    METRICS_ROUTE string = "/metrics"

    // ++++++++
    LABEL_FLOW_NAME     string = "flow_name"
    LABEL_FLOW_ID       string = "flow_id"
    LABEL_FUNCTION_NAME string = "func_name"
    LABEL_FUNCTION_MODE string = "func_mode"

    COUNTER_KISFLOW_DATA_TOTAL_NAME string = "kisflow_data_total"
    COUNTER_KISFLOW_DATA_TOTAL_HELP string = "KisFlow全部Flow的數(shù)據(jù)總量"

    // +++++++ 
    GANGE_FLOW_DATA_TOTAL_NAME string = "flow_data_total"
    GANGE_FLOW_DATA_TOTAL_HELP string = "KisFlow各個FlowID數(shù)據(jù)流的數(shù)據(jù)數(shù)量總量"
)

(3)統(tǒng)計指標(biāo)埋點

作為flow的數(shù)據(jù)總量,我們應(yīng)該在數(shù)據(jù)每次提交源數(shù)據(jù)的時候進行統(tǒng)計即可。

kis-flow/flow/kis_flow_data.go

func (flow *KisFlow) commitSrcData(ctx context.Context) error {

    // 制作批量數(shù)據(jù)batch
    dataCnt := len(flow.buffer)
    batch := make(common.KisRowArr, 0, dataCnt)

    for _, row := range flow.buffer {
        batch = append(batch, row)
    }

    // 清空之前所有數(shù)據(jù)
    flow.clearData(flow.data)

    // 首次提交,記錄flow原始數(shù)據(jù)
    // 因為首次提交,所以PrevFunctionId為FirstVirtual 因為沒有上一層Function
    flow.data[common.FunctionIdFirstVirtual] = batch

    // 清空緩沖Buf
    flow.buffer = flow.buffer[0:0]

    // 首次提交數(shù)據(jù)源數(shù)據(jù),進行統(tǒng)計數(shù)據(jù)總量
    if config.GlobalConfig.EnableProm == true {
        // 統(tǒng)計數(shù)據(jù)總量 Metrics.DataTota 指標(biāo)累計加1
        metrics.Metrics.DataTotal.Add(float64(dataCnt))

        // ++++++++
        //統(tǒng)計當(dāng)前Flow數(shù)量指標(biāo)
        metrics.Metrics.FlowDataTotal.WithLabelValues(flow.Name).Add(float64(dataCnt))
    }

    log.Logger().DebugFX(ctx, "====> After CommitSrcData, flow_name = %s, flow_id = %s\nAll Level Data =\n %+v\n", flow.Name, flow.Id, flow.data)

    return nil
}

所以埋點的位置這之前的統(tǒng)計從數(shù)據(jù)量埋點的位置一樣,只不過在累加數(shù)據(jù)的時候,加上flow.Name標(biāo)簽。

10.5.2 指標(biāo):Flow被調(diào)度次數(shù)

(1)指標(biāo)定義

首先定義指標(biāo)類型,如下:

kis-flow/metrics/kis_metrics.go

// kisMetrics kisFlow的Prometheus監(jiān)控指標(biāo)
type kisMetrics struct {
    // 數(shù)據(jù)數(shù)量總量
    DataTotal prometheus.Counter
    // 各Flow處理數(shù)據(jù)總量
    FlowDataTotal *prometheus.GaugeVec
    // Flow被調(diào)度次數(shù)
    FlowScheduleCntsToTal *prometheus.GaugeVec //++++
}

FlowScheduleCntsToTal 采用 prometheus.GaugeVec類型。主要是為了區(qū)分是哪個Flow產(chǎn)生的數(shù)據(jù)。

(2)指標(biāo)初始化及注冊

kis-flow/metrics/kis_metrics.go

func InitMetrics() {
    Metrics = new(kisMetrics)

    // DataTotal初始化Counter
    Metrics.DataTotal = prometheus.NewCounter(prometheus.CounterOpts{
        Name: common.COUNTER_KISFLOW_DATA_TOTAL_NAME,
        Help: common.COUNTER_KISFLOW_DATA_TOTAL_HELP,
    })

    // FlowDataTotal初始化GaugeVec
    Metrics.FlowDataTotal = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: common.GANGE_FLOW_DATA_TOTAL_NAME,
            Help: common.GANGE_FLOW_DATA_TOTAL_HELP,
        },
        // 標(biāo)簽名稱
        []string{common.LABEL_FLOW_NAME},
    )

    // +++++++++++++
    // FlowScheduleCntsToTal初始化GaugeVec
    Metrics.FlowScheduleCntsToTal = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: common.GANGE_FLOW_SCHE_CNTS_NAME,
            Help: common.GANGE_FLOW_SCHE_CNTS_HELP,
        },
        //標(biāo)簽名稱
        []string{common.LABEL_FLOW_NAME},
    )

    // 注冊Metrics
    prometheus.MustRegister(Metrics.DataTotal)
    prometheus.MustRegister(Metrics.FlowDataTotal) 
    // +++++
    prometheus.MustRegister(Metrics.FlowScheduleCntsToTal)
}

相關(guān)常量定義:

kis-flow/common/const.go


// metrics
const (
    METRICS_ROUTE string = "/metrics"

    LABEL_FLOW_NAME     string = "flow_name"
    LABEL_FLOW_ID       string = "flow_id"
    LABEL_FUNCTION_NAME string = "func_name"
    LABEL_FUNCTION_MODE string = "func_mode"

    COUNTER_KISFLOW_DATA_TOTAL_NAME string = "kisflow_data_total"
    COUNTER_KISFLOW_DATA_TOTAL_HELP string = "KisFlow全部Flow的數(shù)據(jù)總量"

    GANGE_FLOW_DATA_TOTAL_NAME string = "flow_data_total"
    GANGE_FLOW_DATA_TOTAL_HELP string = "KisFlow各個FlowID數(shù)據(jù)流的數(shù)據(jù)數(shù)量總量"

    // +++++++
    GANGE_FLOW_SCHE_CNTS_NAME string = "flow_schedule_cnts"
    GANGE_FLOW_SCHE_CNTS_HELP string = "KisFlow各個FlowID被調(diào)度的次數(shù)"
)

(3)統(tǒng)計指標(biāo)埋點

如果統(tǒng)計每個Flow的調(diào)度次數(shù),我們應(yīng)該在啟動Flow的主入口flow.Run()進行統(tǒng)計,如下:

kis-flow/flow/kis_flow.go

// Run 啟動KisFlow的流式計算, 從起始Function開始執(zhí)行流
func (flow *KisFlow) Run(ctx context.Context) error {

    var fn kis.Function

    fn = flow.FlowHead
    flow.abort = false

    if flow.Conf.Status == int(common.FlowDisable) {
        // flow被配置關(guān)閉
        return nil
    }

    // 因為此時還沒有執(zhí)行任何Function, 所以PrevFunctionId為FirstVirtual 因為沒有上一層Function
    flow.PrevFunctionId = common.FunctionIdFirstVirtual

    // 提交數(shù)據(jù)流原始數(shù)據(jù)
    if err := flow.commitSrcData(ctx); err != nil {
        return err
    }

    // +++++++++++ Metrics
    if config.GlobalConfig.EnableProm == true {
        // 統(tǒng)計Flow的調(diào)度次數(shù)
        metrics.Metrics.FlowScheduleCntsToTal.WithLabelValues(flow.Name).Inc()
    }
    // ++++++++++++++++++++

    // 流式鏈?zhǔn)秸{(diào)用
    for fn != nil && flow.abort == false {

        // flow記錄當(dāng)前執(zhí)行到的Function 標(biāo)記
        fid := fn.GetId()
        flow.ThisFunction = fn
        flow.ThisFunctionId = fid

        // 得到當(dāng)前Function要處理與的源數(shù)據(jù)
        if inputData, err := flow.getCurData(); err != nil {
            log.Logger().ErrorFX(ctx, "flow.Run(): getCurData err = %s\n", err.Error())
            return err
        } else {
            flow.inPut = inputData
        }

        if err := fn.Call(ctx, flow); err != nil {
            // Error
            return err
        } else {
            // Success
            fn, err = flow.dealAction(ctx, fn)
            if err != nil {
                return err
            }

        }
    }

    return nil
}

所以埋點的位置這之前的統(tǒng)計從數(shù)據(jù)量埋點的位置一樣,只不過在累加數(shù)據(jù)的時候,加上flow.Name標(biāo)簽。

10.5.3 指標(biāo):Function被調(diào)度次數(shù)

(1)指標(biāo)定義

首先定義指標(biāo)類型,如下:

kis-flow/metrics/kis_metrics.go

// kisMetrics kisFlow的Prometheus監(jiān)控指標(biāo)
type kisMetrics struct {
    // 數(shù)據(jù)數(shù)量總量
    DataTotal prometheus.Counter
    // 各Flow處理數(shù)據(jù)總量
    FlowDataTotal *prometheus.GaugeVec
    // Flow被調(diào)度次數(shù)
    FlowScheduleCntsToTal *prometheus.GaugeVec 
    // Function被調(diào)度次數(shù)
    FuncScheduleCntsTotal *prometheus.GaugeVec //++++
}

FuncScheduleCntsTotal 采用 prometheus.GaugeVec類型。主要是為了區(qū)分是哪個Function產(chǎn)生的數(shù)據(jù)。

(2)指標(biāo)初始化及注冊

kis-flow/metrics/kis_metrics.go

func InitMetrics() {
    Metrics = new(kisMetrics)

    // DataTotal初始化Counter
    Metrics.DataTotal = prometheus.NewCounter(prometheus.CounterOpts{
        Name: common.COUNTER_KISFLOW_DATA_TOTAL_NAME,
        Help: common.COUNTER_KISFLOW_DATA_TOTAL_HELP,
    })

    // FlowDataTotal初始化GaugeVec
    Metrics.FlowDataTotal = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: common.GANGE_FLOW_DATA_TOTAL_NAME,
            Help: common.GANGE_FLOW_DATA_TOTAL_HELP,
        },
        // 標(biāo)簽名稱
        []string{common.LABEL_FLOW_NAME},
    )

    // FlowScheduleCntsToTal初始化GaugeVec
    Metrics.FlowScheduleCntsToTal = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: common.GANGE_FLOW_SCHE_CNTS_NAME,
            Help: common.GANGE_FLOW_SCHE_CNTS_HELP,
        },
        //標(biāo)簽名稱
        []string{common.LABEL_FLOW_NAME},
    )

    // ++++++++++
    // FuncScheduleCntsTotal初始化GaugeVec
    Metrics.FuncScheduleCntsTotal = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: common.GANGE_FUNC_SCHE_CNTS_NAME,
            Help: common.GANGE_FUNC_SCHE_CNTS_HELP,
        },
        //標(biāo)簽名稱
        []string{common.LABEL_FUNCTION_NAME, common.LABEL_FUNCTION_MODE},
    )

    // 注冊Metrics
    prometheus.MustRegister(Metrics.DataTotal)
    prometheus.MustRegister(Metrics.FlowDataTotal) 
    prometheus.MustRegister(Metrics.FlowScheduleCntsToTal)
    // +++++++
    prometheus.MustRegister(Metrics.FuncScheduleCntsTotal)
}

相關(guān)常量定義:

kis-flow/common/const.go


// metrics
const (
    METRICS_ROUTE string = "/metrics"

    LABEL_FLOW_NAME     string = "flow_name"
    LABEL_FLOW_ID       string = "flow_id"
    LABEL_FUNCTION_NAME string = "func_name"
    LABEL_FUNCTION_MODE string = "func_mode"

    COUNTER_KISFLOW_DATA_TOTAL_NAME string = "kisflow_data_total"
    COUNTER_KISFLOW_DATA_TOTAL_HELP string = "KisFlow全部Flow的數(shù)據(jù)總量"

    GANGE_FLOW_DATA_TOTAL_NAME string = "flow_data_total"
    GANGE_FLOW_DATA_TOTAL_HELP string = "KisFlow各個FlowID數(shù)據(jù)流的數(shù)據(jù)數(shù)量總量"

    GANGE_FLOW_SCHE_CNTS_NAME string = "flow_schedule_cnts"
    GANGE_FLOW_SCHE_CNTS_HELP string = "KisFlow各個FlowID被調(diào)度的次數(shù)"

    // +++++++++ 
    GANGE_FUNC_SCHE_CNTS_NAME string = "func_schedule_cnts"
    GANGE_FUNC_SCHE_CNTS_HELP string = "KisFlow各個Function被調(diào)度的次數(shù)"
)

(3)統(tǒng)計指標(biāo)埋點

如果統(tǒng)計每個Function的調(diào)度次數(shù),我們應(yīng)該在啟動Flow的主入口flow.Run()進行統(tǒng)計,如下:

kis-flow/flow/kis_flow.go

// Run 啟動KisFlow的流式計算, 從起始Function開始執(zhí)行流
func (flow *KisFlow) Run(ctx context.Context) error {

    var fn kis.Function

    fn = flow.FlowHead
    flow.abort = false

    if flow.Conf.Status == int(common.FlowDisable) {
        // flow被配置關(guān)閉
        return nil
    }

    // 因為此時還沒有執(zhí)行任何Function, 所以PrevFunctionId為FirstVirtual 因為沒有上一層Function
    flow.PrevFunctionId = common.FunctionIdFirstVirtual

    // 提交數(shù)據(jù)流原始數(shù)據(jù)
    if err := flow.commitSrcData(ctx); err != nil {
        return err
    }

    if config.GlobalConfig.EnableProm == true {
        // 統(tǒng)計Flow的調(diào)度次數(shù)
        metrics.Metrics.FlowScheduleCntsToTal.WithLabelValues(flow.Name).Inc()
    }

    // 流式鏈?zhǔn)秸{(diào)用
    for fn != nil && flow.abort == false {

        // flow記錄當(dāng)前執(zhí)行到的Function 標(biāo)記
        fid := fn.GetId()
        flow.ThisFunction = fn
        flow.ThisFunctionId = fid

        // ++++++++++++
        fName := fn.GetConfig().FName
        fMode := fn.GetConfig().FMode

        // +++++++++++++++++++++++++++
        if config.GlobalConfig.EnableProm == true {
            // 統(tǒng)計Function調(diào)度次數(shù)
            metrics.Metrics.FuncScheduleCntsTotal.WithLabelValues(fName, fMode).Inc()
        }
        // ++++++++++++++++++++++++++++

        // 得到當(dāng)前Function要處理與的源數(shù)據(jù)
        if inputData, err := flow.getCurData(); err != nil {
            log.Logger().ErrorFX(ctx, "flow.Run(): getCurData err = %s\n", err.Error())
            return err
        } else {
            flow.inPut = inputData
        }

        if err := fn.Call(ctx, flow); err != nil {
            // Error
            return err
        } else {
            // Success
            fn, err = flow.dealAction(ctx, fn)
            if err != nil {
                return err
            }
        }
    }

    return nil
}

在埋點的位置為循環(huán)調(diào)度function的時候,每次在執(zhí)行Funciton的Call()方法之前進行調(diào)度數(shù)據(jù)統(tǒng)計,并且按照fName和fMode進行分組。

10.5.4 指標(biāo):Function執(zhí)行時間

(1)指標(biāo)定義

定義指標(biāo)類型,如下:

kis-flow/metrics/kis_metrics.go

// kisMetrics kisFlow的Prometheus監(jiān)控指標(biāo)
type kisMetrics struct {
    // 數(shù)據(jù)數(shù)量總量
    DataTotal prometheus.Counter
    // 各Flow處理數(shù)據(jù)總量
    FlowDataTotal *prometheus.GaugeVec
    // Flow被調(diào)度次數(shù)
    FlowScheduleCntsToTal *prometheus.GaugeVec 
    // Function被調(diào)度次數(shù)
    FuncScheduleCntsTotal *prometheus.GaugeVec 
    // Function執(zhí)行時間
    FunctionDuration *prometheus.HistogramVec //++++
}

FunctionDuration 采用 prometheus.HistogramVec類型。這是一個不同區(qū)間值的分布統(tǒng)計,不同的時間區(qū)間會落到相應(yīng)的Bucket中。

(2)指標(biāo)初始化及注冊

kis-flow/metrics/kis_metrics.go

func InitMetrics() {
    Metrics = new(kisMetrics)

    // DataTotal初始化Counter
    Metrics.DataTotal = prometheus.NewCounter(prometheus.CounterOpts{
        Name: common.COUNTER_KISFLOW_DATA_TOTAL_NAME,
        Help: common.COUNTER_KISFLOW_DATA_TOTAL_HELP,
    })

    // FlowDataTotal初始化GaugeVec
    Metrics.FlowDataTotal = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: common.GANGE_FLOW_DATA_TOTAL_NAME,
            Help: common.GANGE_FLOW_DATA_TOTAL_HELP,
        },
        // 標(biāo)簽名稱
        []string{common.LABEL_FLOW_NAME},
    )

    // FlowScheduleCntsToTal初始化GaugeVec
    Metrics.FlowScheduleCntsToTal = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: common.GANGE_FLOW_SCHE_CNTS_NAME,
            Help: common.GANGE_FLOW_SCHE_CNTS_HELP,
        },
        //標(biāo)簽名稱
        []string{common.LABEL_FLOW_NAME},
    )

    // FuncScheduleCntsTotal初始化GaugeVec
    Metrics.FuncScheduleCntsTotal = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: common.GANGE_FUNC_SCHE_CNTS_NAME,
            Help: common.GANGE_FUNC_SCHE_CNTS_HELP,
        },
        //標(biāo)簽名稱
        []string{common.LABEL_FUNCTION_NAME, common.LABEL_FUNCTION_MODE},
    )

    // ++++++++++++++++++++++++++
    // FunctionDuration初始化HistogramVec
    Metrics.FunctionDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
        Name:    common.HISTOGRAM_FUNCTION_DURATION_NAME,
        Help:    common.HISTOGRAM_FUNCTION_DURATION_HELP,
        Buckets: []float64{0.005, 0.01, 0.03, 0.08, 0.1, 0.5, 1.0, 5.0, 10, 100, 1000, 5000, 30000}, //單位ms,最大半分鐘
    },
        []string{common.LABEL_FUNCTION_NAME, common.LABEL_FUNCTION_MODE},
    )

    // 注冊Metrics
    prometheus.MustRegister(Metrics.DataTotal)
    prometheus.MustRegister(Metrics.FlowDataTotal) 
    prometheus.MustRegister(Metrics.FlowScheduleCntsToTal)
    prometheus.MustRegister(Metrics.FuncScheduleCntsTotal)
    // +++++++
    prometheus.MustRegister(Metrics.FunctionDuration)
}

相關(guān)常量定義:

kis-flow/common/const.go


// metrics
const (
    METRICS_ROUTE string = "/metrics"

    LABEL_FLOW_NAME     string = "flow_name"
    LABEL_FLOW_ID       string = "flow_id"
    LABEL_FUNCTION_NAME string = "func_name"
    LABEL_FUNCTION_MODE string = "func_mode"

    COUNTER_KISFLOW_DATA_TOTAL_NAME string = "kisflow_data_total"
    COUNTER_KISFLOW_DATA_TOTAL_HELP string = "KisFlow全部Flow的數(shù)據(jù)總量"

    GANGE_FLOW_DATA_TOTAL_NAME string = "flow_data_total"
    GANGE_FLOW_DATA_TOTAL_HELP string = "KisFlow各個FlowID數(shù)據(jù)流的數(shù)據(jù)數(shù)量總量"

    GANGE_FLOW_SCHE_CNTS_NAME string = "flow_schedule_cnts"
    GANGE_FLOW_SCHE_CNTS_HELP string = "KisFlow各個FlowID被調(diào)度的次數(shù)"

    GANGE_FUNC_SCHE_CNTS_NAME string = "func_schedule_cnts"
    GANGE_FUNC_SCHE_CNTS_HELP string = "KisFlow各個Function被調(diào)度的次數(shù)"
    // ++++++++
    HISTOGRAM_FUNCTION_DURATION_NAME string = "func_run_duration"
    HISTOGRAM_FUNCTION_DURATION_HELP string = "Function執(zhí)行耗時"
)

(3)統(tǒng)計指標(biāo)埋點

如果統(tǒng)計每個Function的調(diào)度實行時長,我們應(yīng)該在啟動Flow的主入口flow.Run()進行統(tǒng)計,如下:

kis-flow/flow/kis_flow.go

// Run 啟動KisFlow的流式計算, 從起始Function開始執(zhí)行流
func (flow *KisFlow) Run(ctx context.Context) error {

    var fn kis.Function

    fn = flow.FlowHead
    flow.abort = false

    if flow.Conf.Status == int(common.FlowDisable) {
        // flow被配置關(guān)閉
        return nil
    }
    
    // ++++++++++ Metrics +++++++++
    var funcStart time.Time

    // 因為此時還沒有執(zhí)行任何Function, 所以PrevFunctionId為FirstVirtual 因為沒有上一層Function
    flow.PrevFunctionId = common.FunctionIdFirstVirtual

    // 提交數(shù)據(jù)流原始數(shù)據(jù)
    if err := flow.commitSrcData(ctx); err != nil {
        return err
    }

    if config.GlobalConfig.EnableProm == true {
        // 統(tǒng)計Flow的調(diào)度次數(shù)
        metrics.Metrics.FlowScheduleCntsToTal.WithLabelValues(flow.Name).Inc()
    }

    // 流式鏈?zhǔn)秸{(diào)用
    for fn != nil && flow.abort == false {

        // flow記錄當(dāng)前執(zhí)行到的Function 標(biāo)記
        fid := fn.GetId()
        flow.ThisFunction = fn
        flow.ThisFunctionId = fid

        fName := fn.GetConfig().FName
        fMode := fn.GetConfig().FMode

        if config.GlobalConfig.EnableProm == true {
            // 統(tǒng)計Function調(diào)度次數(shù)
            metrics.Metrics.FuncScheduleCntsTotal.WithLabelValues(fName, fMode).Inc()
            
            // +++++++++++++++
            // 統(tǒng)計Function 耗時 記錄開始時間
            funcStart = time.Now()
        }

        // 得到當(dāng)前Function要處理與的源數(shù)據(jù)
        if inputData, err := flow.getCurData(); err != nil {
            log.Logger().ErrorFX(ctx, "flow.Run(): getCurData err = %s\n", err.Error())
            return err
        } else {
            flow.inPut = inputData
        }

        if err := fn.Call(ctx, flow); err != nil {
            // Error
            return err
        } else {
            // Success
            fn, err = flow.dealAction(ctx, fn)
            if err != nil {
                return err
            }

            // +++++++++++++++
            // 統(tǒng)計Function 耗時
            if config.GlobalConfig.EnableProm == true {
                // Function消耗時間
                duration := time.Since(funcStart)

                // 統(tǒng)計當(dāng)前Function統(tǒng)計指標(biāo),做時間統(tǒng)計
                metrics.Metrics.FunctionDuration.With(
                    prometheus.Labels{
                        common.LABEL_FUNCTION_NAME: fName,
                        common.LABEL_FUNCTION_MODE: fMode}).Observe(duration.Seconds() * 1000)
            }
            // +++++++++++++++

        }
    }

    return nil
}

在埋點的位置每次在執(zhí)行Funciton的Call()方法之前進行起始時間記錄,然后在執(zhí)行Function之后,算出執(zhí)行時間,左后進行統(tǒng)計,按照相對應(yīng)的時間區(qū)間,放入到響應(yīng)的HistogramVec中的bucket中。

10.5.5 指標(biāo):Flow執(zhí)行時間

(1)指標(biāo)定義

定義指標(biāo)類型,如下:

kis-flow/metrics/kis_metrics.go

// kisMetrics kisFlow的Prometheus監(jiān)控指標(biāo)
type kisMetrics struct {
    // 數(shù)據(jù)數(shù)量總量
    DataTotal prometheus.Counter
    // 各Flow處理數(shù)據(jù)總量
    FlowDataTotal *prometheus.GaugeVec
    // Flow被調(diào)度次數(shù)
    FlowScheduleCntsToTal *prometheus.GaugeVec 
    // Function被調(diào)度次數(shù)
    FuncScheduleCntsTotal *prometheus.GaugeVec 
    // Function執(zhí)行時間
    FunctionDuration *prometheus.HistogramVec
    // Flow執(zhí)行時間
    FlowDuration *prometheus.HistogramVec // ++++
}

FlowDuration 采用 prometheus.HistogramVec類型。這是一個不同區(qū)間值的分布統(tǒng)計,不同的時間區(qū)間會落到相應(yīng)的Bucket中。

(2)指標(biāo)初始化及注冊

kis-flow/metrics/kis_metrics.go

func InitMetrics() {
    Metrics = new(kisMetrics)

    // DataTotal初始化Counter
    Metrics.DataTotal = prometheus.NewCounter(prometheus.CounterOpts{
        Name: common.COUNTER_KISFLOW_DATA_TOTAL_NAME,
        Help: common.COUNTER_KISFLOW_DATA_TOTAL_HELP,
    })

    // FlowDataTotal初始化GaugeVec
    Metrics.FlowDataTotal = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: common.GANGE_FLOW_DATA_TOTAL_NAME,
            Help: common.GANGE_FLOW_DATA_TOTAL_HELP,
        },
        // 標(biāo)簽名稱
        []string{common.LABEL_FLOW_NAME},
    )

    // FlowScheduleCntsToTal初始化GaugeVec
    Metrics.FlowScheduleCntsToTal = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: common.GANGE_FLOW_SCHE_CNTS_NAME,
            Help: common.GANGE_FLOW_SCHE_CNTS_HELP,
        },
        //標(biāo)簽名稱
        []string{common.LABEL_FLOW_NAME},
    )

    // FuncScheduleCntsTotal初始化GaugeVec
    Metrics.FuncScheduleCntsTotal = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: common.GANGE_FUNC_SCHE_CNTS_NAME,
            Help: common.GANGE_FUNC_SCHE_CNTS_HELP,
        },
        //標(biāo)簽名稱
        []string{common.LABEL_FUNCTION_NAME, common.LABEL_FUNCTION_MODE},
    )

    // FunctionDuration初始化HistogramVec
    Metrics.FunctionDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
        Name:    common.HISTOGRAM_FUNCTION_DURATION_NAME,
        Help:    common.HISTOGRAM_FUNCTION_DURATION_HELP,
        Buckets: []float64{0.005, 0.01, 0.03, 0.08, 0.1, 0.5, 1.0, 5.0, 10, 100, 1000, 5000, 30000}, //單位ms,最大半分鐘
    },
        []string{common.LABEL_FUNCTION_NAME, common.LABEL_FUNCTION_MODE},
    )
    

    // +++++++++++++
    // FlowDuration初始化HistogramVec
    Metrics.FlowDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    common.HISTOGRAM_FLOW_DURATION_NAME,
            Help:    common.HISTOGRAM_FLOW_DURATION_HELP,
            Buckets: []float64{0.005, 0.01, 0.03, 0.08, 0.1, 0.5, 1.0, 5.0, 10, 100, 1000, 5000, 30000, 60000}, //單位ms,最大1分鐘
        },
        []string{common.LABEL_FLOW_NAME},
    )

    // 注冊Metrics
    prometheus.MustRegister(Metrics.DataTotal)
    prometheus.MustRegister(Metrics.FlowDataTotal) 
    prometheus.MustRegister(Metrics.FlowScheduleCntsToTal)
    prometheus.MustRegister(Metrics.FuncScheduleCntsTotal)
    prometheus.MustRegister(Metrics.FunctionDuration)
    prometheus.MustRegister(Metrics.FlowDuration) // +++++
}

相關(guān)常量定義:

kis-flow/common/const.go


// metrics
const (
    METRICS_ROUTE string = "/metrics"

    LABEL_FLOW_NAME     string = "flow_name"
    LABEL_FLOW_ID       string = "flow_id"
    LABEL_FUNCTION_NAME string = "func_name"
    LABEL_FUNCTION_MODE string = "func_mode"

    COUNTER_KISFLOW_DATA_TOTAL_NAME string = "kisflow_data_total"
    COUNTER_KISFLOW_DATA_TOTAL_HELP string = "KisFlow全部Flow的數(shù)據(jù)總量"

    GANGE_FLOW_DATA_TOTAL_NAME string = "flow_data_total"
    GANGE_FLOW_DATA_TOTAL_HELP string = "KisFlow各個FlowID數(shù)據(jù)流的數(shù)據(jù)數(shù)量總量"

    GANGE_FLOW_SCHE_CNTS_NAME string = "flow_schedule_cnts"
    GANGE_FLOW_SCHE_CNTS_HELP string = "KisFlow各個FlowID被調(diào)度的次數(shù)"

    GANGE_FUNC_SCHE_CNTS_NAME string = "func_schedule_cnts"
    GANGE_FUNC_SCHE_CNTS_HELP string = "KisFlow各個Function被調(diào)度的次數(shù)"
    HISTOGRAM_FUNCTION_DURATION_NAME string = "func_run_duration"
    HISTOGRAM_FUNCTION_DURATION_HELP string = "Function執(zhí)行耗時"

    // ++++++++
    HISTOGRAM_FLOW_DURATION_NAME string = "flow_run_duration"
    HISTOGRAM_FLOW_DURATION_HELP string = "Flow執(zhí)行耗時"
)

(3)統(tǒng)計指標(biāo)埋點

如果統(tǒng)計每個Flow的調(diào)度實行時長,我們應(yīng)該在啟動Flow的主入口flow.Run()進行統(tǒng)計,如下:

kis-flow/flow/kis_flow.go

// Run 啟動KisFlow的流式計算, 從起始Function開始執(zhí)行流
func (flow *KisFlow) Run(ctx context.Context) error {

    var fn kis.Function

    fn = flow.FlowHead
    flow.abort = false

    if flow.Conf.Status == int(common.FlowDisable) {
        // flow被配置關(guān)閉
        return nil
    }
    
    var funcStart time.Time
    // ++++++++++ Metrics +++++++++
    var flowStart time.Time


    // 因為此時還沒有執(zhí)行任何Function, 所以PrevFunctionId為FirstVirtual 因為沒有上一層Function
    flow.PrevFunctionId = common.FunctionIdFirstVirtual

    // 提交數(shù)據(jù)流原始數(shù)據(jù)
    if err := flow.commitSrcData(ctx); err != nil {
        return err
    }

    if config.GlobalConfig.EnableProm == true {
        // 統(tǒng)計Flow的調(diào)度次數(shù)
        metrics.Metrics.FlowScheduleCntsToTal.WithLabelValues(flow.Name).Inc()
        
        // +++++++
        // 統(tǒng)計Flow的執(zhí)行消耗時長
        flowStart = time.Now()
    }

    // 流式鏈?zhǔn)秸{(diào)用
    for fn != nil && flow.abort == false {

        // flow記錄當(dāng)前執(zhí)行到的Function 標(biāo)記
        fid := fn.GetId()
        flow.ThisFunction = fn
        flow.ThisFunctionId = fid

        fName := fn.GetConfig().FName
        fMode := fn.GetConfig().FMode

        if config.GlobalConfig.EnableProm == true {
            // 統(tǒng)計Function調(diào)度次數(shù)
            metrics.Metrics.FuncScheduleCntsTotal.WithLabelValues(fName, fMode).Inc()
            
            // 統(tǒng)計Function 耗時 記錄開始時間
            funcStart = time.Now()
        }

        // 得到當(dāng)前Function要處理與的源數(shù)據(jù)
        if inputData, err := flow.getCurData(); err != nil {
            log.Logger().ErrorFX(ctx, "flow.Run(): getCurData err = %s\n", err.Error())
            return err
        } else {
            flow.inPut = inputData
        }

        if err := fn.Call(ctx, flow); err != nil {
            // Error
            return err
        } else {
            // Success
            fn, err = flow.dealAction(ctx, fn)
            if err != nil {
                return err
            }

            // 統(tǒng)計Function 耗時
            if config.GlobalConfig.EnableProm == true {
                // Function消耗時間
                duration := time.Since(funcStart)

                // 統(tǒng)計當(dāng)前Function統(tǒng)計指標(biāo),做時間統(tǒng)計
                metrics.Metrics.FunctionDuration.With(
                    prometheus.Labels{
                        common.LABEL_FUNCTION_NAME: fName,
                        common.LABEL_FUNCTION_MODE: fMode}).Observe(duration.Seconds() * 1000)
            }
        }
    }

    // +++++++++++++++++++++++++
    // Metrics
    if config.GlobalConfig.EnableProm == true {
        // 統(tǒng)計Flow執(zhí)行耗時
        duration := time.Since(flowStart)
        metrics.Metrics.FlowDuration.WithLabelValues(flow.Name).Observe(duration.Seconds() * 1000)
    }

    return nil
}

在埋點的位置在flow進入Run()方法之后,進行起始時間記錄,然后在Run()最后進行duration統(tǒng)計,統(tǒng)計辦法與Function的統(tǒng)計時長類似。

接下來我們先對DataTotal指標(biāo)做一個單元測試,來進行驗證。

10.6 KieMetrics單元測試(其他Metrics指標(biāo))

10.6.1 新建單元測試

單元測試用例我們復(fù)用之前的TestMetricsDataTotal()方法即可,如下:

kis-flow/test/kis_metrics_test.go

package test

import (
    "context"
    "kis-flow/common"
    "kis-flow/file"
    "kis-flow/kis"
    "kis-flow/test/caas"
    "kis-flow/test/faas"
    "testing"
    "time"
)

func TestMetricsDataTotal(t *testing.T) {
    ctx := context.Background()

    // 0. 注冊Function 回調(diào)業(yè)務(wù)
    kis.Pool().FaaS("funcName1", faas.FuncDemo1Handler)
    kis.Pool().FaaS("funcName2", faas.FuncDemo2Handler)
    kis.Pool().FaaS("funcName3", faas.FuncDemo3Handler)

    // 0. 注冊ConnectorInit 和 Connector 回調(diào)業(yè)務(wù)
    kis.Pool().CaaSInit("ConnName1", caas.InitConnDemo1)
    kis.Pool().CaaS("ConnName1", "funcName2", common.S, caas.CaasDemoHanler1)

    // 1. 加載配置文件并構(gòu)建Flow
    if err := file.ConfigImportYaml("/Users/tal/gopath/src/kis-flow/test/load_conf/"); err != nil {
        panic(err)
    }

    // 2. 獲取Flow
    flow1 := kis.Pool().GetFlow("flowName1")
    
    n := 0

    for n < 10 {
        // 3. 提交原始數(shù)據(jù)
        _ = flow1.CommitRow("This is Data1 from Test")

        // 4. 執(zhí)行flow1
        if err := flow1.Run(ctx); err != nil {
            panic(err)
        }

        time.Sleep(1 * time.Second)
        n++
    }
    
    select {}
}

執(zhí)行單元測試,cd到kis-flow/test/下,執(zhí)行:

go test -test.v -test.paniconexit0 -test.run TestMetricsDataTotal

會得到很多日志輸出,我們等待10s,之后再開啟一個終端,輸入如下指令:

 $ curl http://0.0.0.0:20004/metrics 

得到如下結(jié)果:

# HELP flow_data_total KisFlow各個FlowID數(shù)據(jù)流的數(shù)據(jù)數(shù)量總量
# TYPE flow_data_total gauge
flow_data_total{flow_name="flowName1"} 10
# HELP flow_run_duration Flow執(zhí)行耗時
# TYPE flow_run_duration histogram
flow_run_duration_bucket{flow_name="flowName1",le="0.005"} 0
flow_run_duration_bucket{flow_name="flowName1",le="0.01"} 0
flow_run_duration_bucket{flow_name="flowName1",le="0.03"} 0
flow_run_duration_bucket{flow_name="flowName1",le="0.08"} 0
flow_run_duration_bucket{flow_name="flowName1",le="0.1"} 0
flow_run_duration_bucket{flow_name="flowName1",le="0.5"} 0
flow_run_duration_bucket{flow_name="flowName1",le="1"} 0
flow_run_duration_bucket{flow_name="flowName1",le="5"} 9
flow_run_duration_bucket{flow_name="flowName1",le="10"} 10
flow_run_duration_bucket{flow_name="flowName1",le="100"} 10
flow_run_duration_bucket{flow_name="flowName1",le="1000"} 10
flow_run_duration_bucket{flow_name="flowName1",le="5000"} 10
flow_run_duration_bucket{flow_name="flowName1",le="30000"} 10
flow_run_duration_bucket{flow_name="flowName1",le="60000"} 10
flow_run_duration_bucket{flow_name="flowName1",le="+Inf"} 10
flow_run_duration_sum{flow_name="flowName1"} 29.135023
flow_run_duration_count{flow_name="flowName1"} 10
# HELP flow_schedule_cnts KisFlow各個FlowID被調(diào)度的次數(shù)
# TYPE flow_schedule_cnts gauge
flow_schedule_cnts{flow_name="flowName1"} 10
# HELP func_run_duration Function執(zhí)行耗時
# TYPE func_run_duration histogram
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="0.005"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="0.01"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="0.03"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="0.08"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="0.1"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="0.5"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="1"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="5"} 9
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="10"} 10
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="100"} 10
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="1000"} 10
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="5000"} 10
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="30000"} 10
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="+Inf"} 10
func_run_duration_sum{func_mode="Calculate",func_name="funcName3"} 20.925857
func_run_duration_count{func_mode="Calculate",func_name="funcName3"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="0.005"} 0
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="0.01"} 0
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="0.03"} 0
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="0.08"} 0
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="0.1"} 0
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="0.5"} 0
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="1"} 1
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="5"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="10"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="100"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="1000"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="5000"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="30000"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="+Inf"} 10
func_run_duration_sum{func_mode="Save",func_name="funcName2"} 27.026124
func_run_duration_count{func_mode="Save",func_name="funcName2"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="0.005"} 0
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="0.01"} 0
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="0.03"} 0
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="0.08"} 0
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="0.1"} 0
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="0.5"} 5
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="1"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="5"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="10"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="100"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="1000"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="5000"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="30000"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="+Inf"} 10
func_run_duration_sum{func_mode="Verify",func_name="funcName1"} 4.811095
func_run_duration_count{func_mode="Verify",func_name="funcName1"} 10
# HELP func_schedule_cnts KisFlow各個Function被調(diào)度的次數(shù)
# TYPE func_schedule_cnts gauge
func_schedule_cnts{func_mode="Calculate",func_name="funcName3"} 10
func_schedule_cnts{func_mode="Save",func_name="funcName2"} 10
func_schedule_cnts{func_mode="Verify",func_name="funcName1"} 10

# HELP kisflow_data_total KisFlow全部Flow的數(shù)據(jù)總量
# TYPE kisflow_data_total counter
kisflow_data_total 10

# ... ...



其中我們會發(fā)現(xiàn),我們之前的統(tǒng)計指標(biāo)均已經(jīng)出現(xiàn)了:

  • kisflow_data_total:為總數(shù)據(jù)量,目前是10條數(shù)據(jù),因為我們一共Commit提交了10條源數(shù)據(jù)。
  • flow_data_total:為flow的數(shù)據(jù)總量,目前我們只啟動了flowName1,該數(shù)據(jù)被通緝到了標(biāo)簽flowName1中。
flow_data_total{flow_name="flowName1"} 10
  • flow_schedule_cnts:為flow的調(diào)度次數(shù),因為我們一共執(zhí)行了10次 flow.Run()方法,所以這個調(diào)度次數(shù)是10。
# HELP flow_schedule_cnts KisFlow各個FlowID被調(diào)度的次數(shù)
# TYPE flow_schedule_cnts gauge
flow_schedule_cnts{flow_name="flowName1"} 10
  • func_schedule_cnts: 為各個Function的被調(diào)度次數(shù),這里因為每個Flow會關(guān)聯(lián)3個Function,所以每個Function的調(diào)度次數(shù)應(yīng)該和Flow1的調(diào)度次數(shù)相同,都是10。
# HELP func_schedule_cnts KisFlow各個Function被調(diào)度的次數(shù)
# TYPE func_schedule_cnts gauge
func_schedule_cnts{func_mode="Calculate",func_name="funcName3"} 10
func_schedule_cnts{func_mode="Save",func_name="funcName2"} 10
func_schedule_cnts{func_mode="Verify",func_name="funcName1"} 10
  • func_run_duration_bucket: 為Function的執(zhí)行耗時分布統(tǒng)計。(有關(guān)HISTOGRAM 的統(tǒng)計方式比較復(fù)雜,這里就不贅述了,有興趣的開發(fā)者可以去查閱一些相關(guān)資料。)
# HELP func_run_duration Function執(zhí)行耗時
# TYPE func_run_duration histogram
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="0.005"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="0.01"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="0.03"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="0.08"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="0.1"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="0.5"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="1"} 0
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="5"} 9
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="10"} 10
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="100"} 10
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="1000"} 10
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="5000"} 10
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="30000"} 10
func_run_duration_bucket{func_mode="Calculate",func_name="funcName3",le="+Inf"} 10
func_run_duration_sum{func_mode="Calculate",func_name="funcName3"} 20.925857
func_run_duration_count{func_mode="Calculate",func_name="funcName3"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="0.005"} 0
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="0.01"} 0
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="0.03"} 0
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="0.08"} 0
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="0.1"} 0
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="0.5"} 0
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="1"} 1
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="5"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="10"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="100"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="1000"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="5000"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="30000"} 10
func_run_duration_bucket{func_mode="Save",func_name="funcName2",le="+Inf"} 10
func_run_duration_sum{func_mode="Save",func_name="funcName2"} 27.026124
func_run_duration_count{func_mode="Save",func_name="funcName2"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="0.005"} 0
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="0.01"} 0
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="0.03"} 0
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="0.08"} 0
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="0.1"} 0
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="0.5"} 5
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="1"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="5"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="10"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="100"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="1000"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="5000"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="30000"} 10
func_run_duration_bucket{func_mode="Verify",func_name="funcName1",le="+Inf"} 10
func_run_duration_sum{func_mode="Verify",func_name="funcName1"} 4.811095
func_run_duration_count{func_mode="Verify",func_name="funcName1"} 10
  • flow_run_duration_bucket: 為為Flow的執(zhí)行耗時分布統(tǒng)計。(有關(guān)HISTOGRAM 的統(tǒng)計方式比較復(fù)雜,這里就不贅述了,有興趣的開發(fā)者可以去查閱一些相關(guān)資料。)
# HELP flow_run_duration Flow執(zhí)行耗時
# TYPE flow_run_duration histogram
flow_run_duration_bucket{flow_name="flowName1",le="0.005"} 0
flow_run_duration_bucket{flow_name="flowName1",le="0.01"} 0
flow_run_duration_bucket{flow_name="flowName1",le="0.03"} 0
flow_run_duration_bucket{flow_name="flowName1",le="0.08"} 0
flow_run_duration_bucket{flow_name="flowName1",le="0.1"} 0
flow_run_duration_bucket{flow_name="flowName1",le="0.5"} 0
flow_run_duration_bucket{flow_name="flowName1",le="1"} 0
flow_run_duration_bucket{flow_name="flowName1",le="5"} 9
flow_run_duration_bucket{flow_name="flowName1",le="10"} 10
flow_run_duration_bucket{flow_name="flowName1",le="100"} 10
flow_run_duration_bucket{flow_name="flowName1",le="1000"} 10
flow_run_duration_bucket{flow_name="flowName1",le="5000"} 10
flow_run_duration_bucket{flow_name="flowName1",le="30000"} 10
flow_run_duration_bucket{flow_name="flowName1",le="60000"} 10
flow_run_duration_bucket{flow_name="flowName1",le="+Inf"} 10
flow_run_duration_sum{flow_name="flowName1"} 29.135023
flow_run_duration_count{flow_name="flowName1"} 10

10.7 有關(guān)KisFlow的Metrics的Grafana看板展示

既然有了Prometheus的指標(biāo)統(tǒng)計,我們可以給KisFlow的流式計算程序結(jié)合Grafana進行看板展示。
由于各個開發(fā)者的項目的統(tǒng)計指標(biāo)和看板要求不一定相同,這里本文就不提供具體的Grafana看板的配置文件了,下面提供一個KisFlow的項目看板,作為演示參考,如下:

10.8 【V0.9】 源代碼

https://github.com/aceld/kis-flow/releases/tag/v0.9


作者:劉丹冰Aceld github: https://github.com/aceld
KisFlow開源項目地址:https://github.com/aceld/kis-flow

Golang框架實戰(zhàn)-KisFlow流式計算框架專欄

Golang框架實戰(zhàn)-KisFlow流式計算框架(1)-概述
Golang框架實戰(zhàn)-KisFlow流式計算框架(2)-項目構(gòu)建/基礎(chǔ)模塊-(上)
Golang框架實戰(zhàn)-KisFlow流式計算框架(3)-項目構(gòu)建/基礎(chǔ)模塊-(下)
Golang框架實戰(zhàn)-KisFlow流式計算框架(4)-數(shù)據(jù)流
Golang框架實戰(zhàn)-KisFlow流式計算框架(5)-Function調(diào)度
Golang框架實戰(zhàn)-KisFlow流式計算框架(6)-Connector
Golang框架實戰(zhàn)-KisFlow流式計算框架(7)-配置導(dǎo)入與導(dǎo)出
Golang框架實戰(zhàn)-KisFlow流式計算框架(8)-KisFlow Action
Golang框架實戰(zhàn)-KisFlow流式計算框架(10)-Flow多副本

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內(nèi)容