#GOMAXPROCS 与容器的相处之道

#引言

众所周知,GOMAXPROCS 是 Golang 提供的非常重要的一个环境变量设定。通过设定 GOMAXPROCS,用户可以调整 Runtime Scheduler 中 Processor(简称P)的数量。由于每个系统线程,必须要绑定 P 才能真正地进行执行。所以 P 的数量会很大程度上影响 Golang Runtime 的并发表现。GOMAXPROCS 在目前版本(1.12)的默认值是 CPU 核数,而以 Docker 为代表的容器虚拟化技术,会通过 cgroup 等技术对 CPU 资源进行隔离。以 Kubernetes 为代表的基于容器虚拟化实现的资源管理系统,也支持这样的特性。这类技术对 CPU 的隔离限制,是否能够影响到 Golang 中的 GOMAXPROCS,进而影响到 Golang Runtime 的并发表现呢?这是一个值得探索的话题,本文从 Docker 和 Kubernetes 对 CPU 资源的限制出发,利用实验的方式验证了这一问题,并且给出了一些个人看法。

#背景

Goroutines 是 Golang 最吸引人的特性之一,它是 stackful coroutines 的一种实现。为了支持这一特性,Golang 需要一个运行时,在 Goroutines 和系统线程之间进行调度,这也就是 go-scheduler 的作用。go-scheduler 引入了三个抽象,分别是 Processor,Machine(简称 M) 和 Goroutine(简称 G)。其中 G 就是用户创建的 goroutines,而 M 则是系统线程,是负责真正执行 goroutines 的系统线程。 Processor 是类似于 CPU 核心的概念,其用来控制并发的 M 数量。

当 M 需要执行 G 的时候,它需要寻找到一个空闲的 P,只有跟一个 P 绑定后,M 才能被执行。通过这样的方式,go-scheduler 保证了在同一时间内,最多只有 P 个系统线程在真正地执行。P 的数量在默认情况下,会被设定为 CPU 的数量。而 M 虽然需要跟 P 绑定执行,但数量上并不与 P 相等。这是因为 M 会因为系统调用或者其他事情被阻塞,因此随着程序的执行,M 的数量可能增长,而 P 在没有用户干预的情况下,则会保持不变。

后续原文