在容器里設(shè)置GOMAXPROCS的正確姿勢(shì)
GOMAXPROCS 是 Go 提供的非常重要的一個(gè)環(huán)境變量。通過(guò)設(shè)定 GOMAXPROCS,用戶可以調(diào)整調(diào)度器中 Processor(簡(jiǎn)稱P)的數(shù)量。由于每個(gè)系統(tǒng)線程必須要綁定 P ,P 才能把 G 交給 M 執(zhí)行。如下圖所示
所以 P 的數(shù)量會(huì)很大程度上影響 Go Runtime 的并發(fā)表現(xiàn)。GOMAXPROCS 在 Go 1.5 版本后的默認(rèn)值是機(jī)器的 CPU 核數(shù) (runtime.NumCPU)。通過(guò)下面的代碼片段可以獲取當(dāng)前機(jī)器的核心數(shù)和給 GOMAXPROCS 設(shè)置的值。
package main
import (
"fmt"
"runtime"
)
func getGOMAXPROCS() int {
_ := runtime.NumCPU() // 獲取機(jī)器的CPU核心數(shù)
return runtime.GOMAXPROCS(0) // 參數(shù)為零時(shí)用于獲取給GOMAXPROCS設(shè)置的值
}
func main() {
fmt.Printf("GOMAXPROCS: %d\n", getGOMAXPROCS())
}
而以 Docker 為代表的容器虛擬化技術(shù),會(huì)通過(guò) cgroup 等技術(shù)對(duì) CPU 資源進(jìn)行隔離。以 Kubernetes 為代表的基于容器虛擬化實(shí)現(xiàn)的資源管理系統(tǒng),也支持這樣的特性,比如下面這個(gè) PodTemplate 的容器的定義里 limits.cpu = 1000m 就代表給這個(gè)容器分配1個(gè)核心的使用時(shí)間。

這類技術(shù)對(duì) CPU 的隔離限制,導(dǎo)致 runtime.NumCPU() 無(wú)法正確獲取到容器被分配的 CPU 資源數(shù)。runtime.NumCPU()獲取的是宿主機(jī)的核心數(shù)。
設(shè)置 GOMAXPROCS 高于真正可使用的核心數(shù)后會(huì)導(dǎo)致Go調(diào)度器不停地進(jìn)行OS線程切換,從而給調(diào)度器增加很多不必要的工作。
目前 Go 官方并無(wú)好的方式來(lái)規(guī)避在容器里獲取不到真正可使用的核心數(shù)這一問(wèn)題,而 Uber 提出了一種 Workaround 方法,利用 uber-go/automaxprocs 這一個(gè)包,可以在運(yùn)行時(shí)根據(jù) cgroup 為容器分配的CPU資源限制數(shù)來(lái)修改 GOMAXPROCS。
import _ "go.uber.org/automaxprocs"
func main() {
// Your application logic here.
}
參考資料:
https://gaocegege.com/Blog/maxprocs-cpu
https://stackoverflow.com/questions/36492356/parallel-programming-in-go-using-gomaxprocs/36492517#36492517
推薦閱讀
