從telegraf改造談golang多協程精確控制

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

從telegraf改造談golang多協程精確控制

前言

telegraf是infuxdb公司開源出來的一個基於外掛程式機制的收集metrics的項目。整個架構和elastic公司的日誌收集系統極其類似,具備良好的擴充性。與現在流行的各種exporter+promethues監控方案相比:

  1. 大致具備良好的可擴充性。很容易增加自己的處理邏輯,在input,output,process,filter等環境定製自己專屬的外掛程式。
  2. 統一了各種exporter,減少了部署各種exporter的工作量和維護成本。

目前telegraf改造工作基本上是兩大部分:

  1. 增加了一些telegraf不支援的外掛程式,比如虛擬化(kvm,vmware等),資料庫(oracle),k8s和openstack等input外掛程式。
  2. telegraf是基於設定檔的,所以會有兩個問題,很難做分布式和無停機動態調度input任務。所以我們的工作就是將擷取配置介面化,所有的設定檔來源於統一配置中心。然後就是改造無停機動態調度input。

在改造改造無停機動態調度input就涉及到golang多協程精確控制的問題。

一些golang常用並發手段

sync包下WaitGroup

具體案例:

    var wg sync.WaitGroup    wg.Add(len(a.Config.Outputs))    for _, o := range a.Config.Outputs {        go func(output *models.RunningOutput) {            defer wg.Done()            err := output.Write()            if err != nil {                log.Printf("E! Error writing to output [%s]: %s\n",                    output.Name, err.Error())            }        }(o)    }    wg.Wait()

WaitGroup內部維護了一個counter,當counter數值為0時,表明添加的任務都已經完成。
總共有三個方法:

func (wg *WaitGroup) Add(delta int)

新增工作,delta參數表示新增工作的數量。

func (wg *WaitGroup) Done()

任務執行完成,調用Done方法,一般使用姿勢都是defer wg.Done(),此時counter中會減一。

func (wg *WaitGroup) Wait()

通過使用sync.WaitGroup,可以阻塞主線程,直到相應數量的子線程結束。

chan struct{},控制協程退出

啟動協程的時候,傳遞一個shutdown chan struct{},需要關閉該協程的時候,直接close(shutdown)。struct{}在golang中是一個消耗接近0的對象。
具體案例:

// gatherer runs the inputs that have been configured with their own// reporting interval.func (a *Agent) gatherer(    shutdown chan struct{},    kill chan struct{},    input *models.RunningInput,    interval time.Duration,    metricC chan telegraf.Metric,) {    defer panicRecover(input)    GatherTime := selfstat.RegisterTiming("gather",        "gather_time_ns",        map[string]string{"input": input.Config.Name},    )    acc := NewAccumulator(input, metricC)    acc.SetPrecision(a.Config.Agent.Precision.Duration,        a.Config.Agent.Interval.Duration)    ticker := time.NewTicker(interval)    defer ticker.Stop()    for {        internal.RandomSleep(a.Config.Agent.CollectionJitter.Duration, shutdown)        start := time.Now()        gatherWithTimeout(shutdown, kill, input, acc, interval)        elapsed := time.Since(start)        GatherTime.Incr(elapsed.Nanoseconds())        select {        case <-shutdown:            return        case <-kill:            return        case <-ticker.C:            continue        }    }}

藉助chan 實現指定數量的協程或動態調整協程數量

當然這裡必須是每個協程是等冪,也就是所有協程做的是同樣的工作。
首先建立 一個 pool:= make(chan chan struct{}, maxWorkers),maxWorkers為目標協程數量。
然後啟動協程:

    for i := 0; i < s.workers; i++ {        go func() {            wQuit := make(chan struct{})            s.pool <- wQuit            s.sFlowWorker(wQuit)        }()    }

關閉協程:

func (s *SFlow) sFlowWorker(wQuit chan struct{}) {LOOP:    for {        select {        case <-wQuit:            break LOOP        case msg, ok = <-sFlowUDPCh:            if !ok {                break LOOP            }        }        // 此處執行任務操作        }

動態調整:

            for n = 0; n < 10; n++ {                if len(s.pool) > s.workers {                    wQuit := <-s.pool                    close(wQuit)                }            }

多協程精確控制

在改造telegraf過程中,要想動態調整input,每個input都是唯一的,分屬不同類型外掛程式。就必須實現精準控制指定的協程的啟停。
這個時候實現思路就是:實現一個kills map[string]chan struct{},k為每個任務的唯一ID。新增工作時候,傳遞一個chan struct{},這個時候關閉指定ID的chan struct{},就能控制指定的協程。

// DelInput add inputfunc (a *Agent) DelInput(inputs []*models.RunningInput) error {    a.storeMutex.Lock()    defer a.storeMutex.Unlock()    for _, v := range inputs {        if _, ok := a.kills[v.Config.ID]; !ok {            return fmt.Errorf("input: %s,未找到,無法刪除", v.Config.ID)        }    }    for _, input := range inputs {        if kill, ok := a.kills[input.Config.ID]; ok {            delete(a.kills, input.Config.ID)            close(kill)        }    }    return nil}

新增工作:

// AddInput add inputfunc (a *Agent) AddInput(shutdown chan struct{}, inputs []*models.RunningInput) error {    a.storeMutex.Lock()    defer a.storeMutex.Unlock()    for _, v := range inputs {        if _, ok := a.kills[v.Config.ID]; ok {            return fmt.Errorf("input: %s,已經存在無法新增", v.Config.ID)        }    }    for _, input := range inputs {        interval := a.Config.Agent.Interval.Duration        // overwrite global interval if this plugin has it's own.        if input.Config.Interval != 0 {            interval = input.Config.Interval        }        if input.Config.ID == "" {            continue        }                a.wg.Add(1)        kill := make(chan struct{})        a.kills[input.Config.ID] = kill        go func(in *models.RunningInput, interv time.Duration) {            defer a.wg.Done()            a.gatherer(shutdown, kill, in, interv, a.metricC)        }(input, interval)    }    return nil}

總結

簡單介紹了一下telegraf項目。後續的最佳化和改造工作還在繼續。主要是分布式telegraf的調度演算法。畢竟集中化所有exporter以後,telegraf的負載能力受單機能力限制,而且也不符合高可用的使用目標。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.