Kubernetes源码-pkg-01-wait-定时(条件)轮询库
前言
在前面的主要组件分析过程中,有数次提及到的wait库让我记忆犹新,这是一个被高频引用的库,各个主要组件如scheduler、controller、kubelet等都常常使用wait库中的function轮询间隔(或条件)触发执行动作。整个wait库只有一个代码文件,代码行数不过400余行,本篇就来完整地分析一下这个库。
代码路径: vendor/k8s.io/apimachinery/pkg/util/wait/wait.go
分类
wait库内的各种function,大体来说都是以轮询的形式,根据时间间隔、条件判断,来确定工具执行函数是否应被继续执行。按代码中呈现,按触发形式再细化一下,各function则可以分为这几类:
条件类型 | 说明 |
---|---|
Until类 | 用得最多的类型,一般以一条chan struct{} 或context Done接收done信号作为终止轮询的依据 |
Backoff类 | 每间隔一定的时长执行一次回溯函数,一般情况下,间隔时长随着回溯次数递增而倍数级延长,但间隔时长也会有上限值 |
poll类 | 两条channel,一条用作传递单次执行信号用来轮询,一条用作传递done信号 |
Until类
Untile类型有两个具体实现,分别是Until和UntilWithContext
Until函数
vendor/k8s.io/apimachinery/pkg/util/wait/wait.go:87
1 | func Until(f func(), period time.Duration, stopCh <-chan struct{}) { |
调用的是JitterUntil函数
1 | func JitterUntil(f func(), period time.Duration, jitterFactor float64, sliding bool, stopCh <-chan struct{}) { |
1 | func Jitter(duration time.Duration, maxFactor float64) time.Duration { |
JitterUntil函数可谓是把条件考虑得很细致,参数上有执行周期、抖动因子、窗口期(是否包含函数执行时间),另外在stopCh信号处理上也做到了预防超期执行,JitterUntil函数已经足以应对各类以时间间隔维度的轮询场景了。
UntilWithContext
vendor/k8s.io/apimachinery/pkg/util/wait/wait.go:96
1 | func UntilWithContext(ctx context.Context, f func(context.Context), period time.Duration) { |
1 | func JitterUntilWithContext(ctx context.Context, f func(context.Context), period time.Duration, jitterFactor float64, sliding bool) { |
可以看到,最后也是调用的JitterUntil,唯一的不同只是参数stopCh换成了包了一层的context.Done()
Backoff类
Backoff结构体
Backoff类函数都有一个类型为Backoff结构体的形参,先来看看这个结构体的定义
vendor/k8s.io/apimachinery/pkg/util/wait/wait.go:207
1 | type Backoff struct { |
1 | func (b *Backoff) Step() time.Duration { |
ok,下面来看看具体的Backoff类实现函数。
ExponentialBackoff函数
1 | func ExponentialBackoff(backoff Backoff, condition ConditionFunc) error { |
ExponentialBackoff函数的工作逻辑是:
在最外层限定了backoff函数的最多重复执行次数,即等于Steps字段值
过程中condition()条件函数执行异常或正常则直接返回,否则按最大限定次数,每次轮询等待时间参照Step()方法返回值进行
- 到达上限次数后若条件函数仍未返回结果,则返回超时错误
显然,此函数适用于失败重试的场景,常用k8s的同学一定不会陌生,我们常遇到pod多次失败重试的状态,如:CrashLoopBackOff/ImagePullBackOff状态,重试过程正是使用此函数封装的
poll类
WaitFunc结构体
1 | type WaitFunc func(done <-chan struct{}) <-chan struct{} |
这里定义的type WaitFunc下面都会用到,接收的参数chan用作done信号传递,返回的chan用作执行信号传递
Poll
vendor/k8s.io/apimachinery/pkg/util/wait/wait.go:286
1 | func Poll(interval, timeout time.Duration, condition ConditionFunc) error { |
调用的pollInternal函数,看命名就知道是间隔执行的意思:
1 | func pollInternal(wait WaitFunc, condition ConditionFunc) error { |
–>
1 | func WaitFor(wait WaitFunc, fn ConditionFunc, done <-chan struct{}) error { |
WaitFor函数可实现按条件结果来决定对执行函数的执行次数、结束时机的控制,实现更高的可控性,但与之对应的是,执行信号、结束信号的发送全部需要在type waitFunc内部实现。即上面的Poll()函数中的poller(interval, timeout)
是waitFunc
的实现,来看看:
1 | func poller(interval, timeout time.Duration) WaitFunc { |
从pollInternal的实现上来看,看起来与Until差别不大,但在WaitFunc的实现层面,除了像poller()函数这样以定时器为条件外,也可以取其他更加灵活的条件作为判定往执行chan发送信号的依据。