記一次golang程序CPU高的排查過程

前言

事情的原由是某天CTO忽然和我說,生產環境的程序CPU有點高,關鍵是如今也沒什麼負載,一樣的代碼在開發環境上CPU就低的多了。linux

不用細說,那必定是有什麼地方出問題了。golang

CTO還說,他pprof過了,佔用CPU最高的runtime.futex,還發了一篇相關的文章誰佔了該CPU核的30% - 一個較意外的Go性能問題 ,打趣說沒準系統負載高了,這個問題就沒了。由於原文中寫到:bash

必須指出,本問題是由於系統空閒沒有goroutine能夠調度形成的。顯然的,系統繁忙的時候,即CPU資源真正體現價值時,上述30%的%CPU的overhead並不存在,由於大機率下會有goroutine可供調度,無需去作讓M去sleep這個很重的操做。服務器

而後就把這個鍋就「甩」給我了,讓我研究一下。畢竟開發環境的負載也沒有那麼高,可是CPU卻蠻正常的。app

分析

一開始我是沒什麼頭緒,順着CTO提供的線索,搜索了一些runtime.futex的文章,幾乎全部文章都會提到如下可能會使CPU佔用率高的示例代碼:性能

var ticker = time.NewTicker(100 * time.Millisecond)
    defer ticker.Stop()
    var counter = 0
    for {
        select {
        case <-serverDone:
            return
        case <-ticker.C:
            counter += 1
        }
    }
複製代碼

這段代碼給我指明瞭一些方向,我開始全局搜索一些time.NewTicker的代碼。ui

巧的是,還真讓我搜到了一些,其中一個ticker的時間設置的頗有問題。spa

options = append(options, metrics.BatchInterval(time.Duration(conf.BatchInterval)))
複製代碼

這裏的time.Duration(conf.BatchInterval)沒有指定單位,那可就是nano second(納秒)級別的,這ticker的協程跑起來,沒形成死鎖,只能說linux服務器的性能好。code

後來,順藤摸瓜,發現了這個interval實際上是promethus的採樣interval,promethus只在生產打開了,也能夠解釋了爲何一樣的代碼只在生產上出問題。server

解決方法

初步的解決方法很簡單,就是給這個interval加上單位,再略微調大一些就好,並且目前咱們並無過重視promethus的性能數據,因此也不是很肯定50ms的採樣間隔是否是有些過大。

總結

雖然說找到了問題的root cause,但仍是有值得改進的地方,好比說,若是一開始就先diff生產和開發的程序的配置有哪些不一樣,說不定能夠更快的解決問題。

參考文章

相關文章
相關標籤/搜索