WorkQueue 深度分析(下篇)--- 限流队列、限流器、指标、并行化
基于
client-go v0.36.1util/workqueue/源码
五、核心业务逻辑深度解析 --- 限流队列 (rate_limiting_queue.go)
5.1 rateLimitingType --- 限流队列实现
go
type rateLimitingType[T comparable] struct {
TypedDelayingInterface[T] // 嵌入延迟队列
rateLimiter TypedRateLimiter[T] // 限流器
}
AddRateLimited --- 限流入队
go
func (q *rateLimitingType[T]) AddRateLimited(item T) {
// 1. rateLimiter.When(item) 计算该 item 应该等待的延迟
// 基于该 item 的历史失败次数
// 2. AddAfter 延迟入队
q.TypedDelayingInterface.AddAfter(item, q.rateLimiter.When(item))
}
调用链 :AddRateLimited(item) → rateLimiter.When(item) → duration → AddAfter(item, duration) → waitingLoop → 到期后 Add(item) → 基础队列
Forget --- 清除失败计数
go
func (q *rateLimitingType[T]) Forget(item T) {
// 清除 item 的失败计数
// 不再跟踪该 item 的重试历史
// 注意:Forget 不调用 Done!必须单独调用 Done
q.rateLimiter.Forget(item)
}
NumRequeues --- 查询重试次数
go
func (q *rateLimitingType[T]) NumRequeues(item T) int {
return q.rateLimiter.NumRequeues(item)
}
5.2 限流队列完整使用模式
Queue DelayingQueue RateLimiter RateLimitingQueue EventHandler Queue DelayingQueue RateLimiter RateLimitingQueue EventHandler #mermaid-svg-ZGOFxjVc6jtCEZL6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZGOFxjVc6jtCEZL6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .error-icon{fill:#552222;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .marker.cross{stroke:#333333;}#mermaid-svg-ZGOFxjVc6jtCEZL6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZGOFxjVc6jtCEZL6 p{margin:0;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZGOFxjVc6jtCEZL6 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ZGOFxjVc6jtCEZL6 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ZGOFxjVc6jtCEZL6 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .sequenceNumber{fill:white;}#mermaid-svg-ZGOFxjVc6jtCEZL6 #sequencenumber{fill:#333;}#mermaid-svg-ZGOFxjVc6jtCEZL6 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .messageText{fill:#333;stroke:none;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .labelText,#mermaid-svg-ZGOFxjVc6jtCEZL6 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .loopText,#mermaid-svg-ZGOFxjVc6jtCEZL6 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ZGOFxjVc6jtCEZL6 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .noteText,#mermaid-svg-ZGOFxjVc6jtCEZL6 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .actorPopupMenu{position:absolute;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ZGOFxjVc6jtCEZL6 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZGOFxjVc6jtCEZL6 .actor-man circle,#mermaid-svg-ZGOFxjVc6jtCEZL6 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ZGOFxjVc6jtCEZL6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 成功场景 Worker 处理 key... 清除失败计数 失败重试场景 Worker 处理 key... 失败 Worker 调用 AddRateLimited 重试 指数退避: 5ms→10ms→20ms→40ms→...→1000s上限 Add(key)Add(key)Get() → keyDone(key)Forget(key)AddRateLimited(key)When(key) → 5ms (第1次失败)AddAfter(key, 5ms)Add(key) 5ms后Get() → keyDone(key)AddRateLimited(key)When(key) → 10ms (第2次失败)AddAfter(key, 10ms)
六、限流器详解 (default_rate_limiters.go)
6.1 TypedRateLimiter 接口
go
type TypedRateLimiter[T comparable] interface {
When(item T) time.Duration // item 应该等待多久
Forget(item T) // 清除 item 的跟踪状态
NumRequeues(item T) int // item 被重试了多少次
}
6.2 ItemExponentialFailureRateLimiter --- 指数退避限流器
这是最常用的限流器 ,也是 DefaultControllerRateLimiter 的核心组件。
go
type TypedItemExponentialFailureRateLimiter[T comparable] struct {
failuresLock sync.Mutex // 保护 failures map
failures map[T]int // 每个 item 的失败次数
baseDelay time.Duration // 基础延迟(默认 5ms)
maxDelay time.Duration // 最大延迟(默认 1000s)
}
When --- 计算延迟
go
func (r *TypedItemExponentialFailureRateLimiter[T]) When(item T) time.Duration {
r.failuresLock.Lock()
defer r.failuresLock.Unlock()
// ─── 步骤1: 获取当前失败次数 ───
exp := r.failures[item]
// ─── 步骤2: 递增失败计数 ───
r.failures[item] = r.failures[item] + 1
// 注意:即使本次不使用该延迟,计数也已递增
// ─── 步骤3: 计算指数退避 ───
backoff := float64(r.baseDelay.Nanoseconds()) * math.Pow(2, float64(exp))
// baseDelay × 2^exp
// exp=0: 5ms × 1 = 5ms
// exp=1: 5ms × 2 = 10ms
// exp=2: 5ms × 4 = 20ms
// exp=3: 5ms × 8 = 40ms
// ...
// exp=18: 5ms × 262144 ≈ 1310s → 被 maxDelay 截断
// ─── 步骤4: 溢出保护 ───
if backoff > math.MaxInt64 {
return r.maxDelay
}
// ─── 步骤5: 最大延迟截断 ───
calculated := time.Duration(backoff)
if calculated > r.maxDelay {
return r.maxDelay
}
return calculated
}
指数退避曲线(baseDelay=5ms, maxDelay=1000s):
#mermaid-svg-L5ynRgcjie0LjJ5D{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-L5ynRgcjie0LjJ5D .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-L5ynRgcjie0LjJ5D .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-L5ynRgcjie0LjJ5D .error-icon{fill:#552222;}#mermaid-svg-L5ynRgcjie0LjJ5D .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-L5ynRgcjie0LjJ5D .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-L5ynRgcjie0LjJ5D .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-L5ynRgcjie0LjJ5D .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-L5ynRgcjie0LjJ5D .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-L5ynRgcjie0LjJ5D .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-L5ynRgcjie0LjJ5D .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-L5ynRgcjie0LjJ5D .marker{fill:#333333;stroke:#333333;}#mermaid-svg-L5ynRgcjie0LjJ5D .marker.cross{stroke:#333333;}#mermaid-svg-L5ynRgcjie0LjJ5D svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-L5ynRgcjie0LjJ5D p{margin:0;}#mermaid-svg-L5ynRgcjie0LjJ5D :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 指数退避延迟曲线 02468101214161820 失败次数 (exp) 10009008007006005004003002001000 延迟 (秒)
Forget / NumRequeues
go
func (r *TypedItemExponentialFailureRateLimiter[T]) Forget(item T) {
r.failuresLock.Lock()
defer r.failuresLock.Unlock()
delete(r.failures, item)
// 从 failures map 中删除
// 下次 When(item) 会从 exp=0 重新开始
}
func (r *TypedItemExponentialFailureRateLimiter[T]) NumRequeues(item T) int {
r.failuresLock.Lock()
defer r.failuresLock.Unlock()
return r.failures[item]
}
6.3 BucketRateLimiter --- 令牌桶限流器
go
type TypedBucketRateLimiter[T comparable] struct {
*rate.Limiter // 嵌入 golang.org/x/time/rate.Limiter
}
func (r *TypedBucketRateLimiter[T]) When(item T) time.Duration {
return r.Limiter.Reserve().Delay()
// 预约一个令牌,返回需要等待的时间
// 令牌桶参数:QPS=10, Burst=100
// 这是全局限流,不区分 item
}
func (r *TypedBucketRateLimiter[T]) NumRequeues(item T) int { return 0 }
// 令牌桶不跟踪单个 item 的重试次数
func (r *TypedBucketRateLimiter[T]) Forget(item T) {}
// 令牌桶无需 Forget
6.4 DefaultControllerRateLimiter --- 默认组合限流器
go
func DefaultTypedControllerRateLimiter[T comparable]() TypedRateLimiter[T] {
return NewTypedMaxOfRateLimiter(
// 组件1: 单 item 指数退避
// baseDelay=5ms, maxDelay=1000s
NewTypedItemExponentialFailureRateLimiter[T](5*time.Millisecond, 1000*time.Second),
// 组件2: 全局令牌桶
// QPS=10, Burst=100
&TypedBucketRateLimiter[T]{Limiter: rate.NewLimiter(rate.Limit(10), 100)},
)
}
MaxOfRateLimiter 取两者的最大值:
go
func (r *TypedMaxOfRateLimiter[T]) When(item T) time.Duration {
ret := time.Duration(0)
for _, limiter := range r.limiters {
curr := limiter.When(item)
if curr > ret {
ret = curr
// 取所有限流器的最大延迟
}
}
return ret
}
双层限流的效果:
| 场景 | 指数退避延迟 | 令牌桶延迟 | 最终延迟 (Max) |
|---|---|---|---|
| 第1次失败 (exp=0) | 5ms | ~0ms (桶内有令牌) | 5ms |
| 第2次失败 (exp=1) | 10ms | ~0ms | 10ms |
| 第5次失败 (exp=4) | 80ms | ~0ms | 80ms |
| 快速连续重试 | 5ms | ~100ms (桶空) | 100ms |
| 大量 item 同时重试 | 各自指数退避 | 令牌桶限制整体 QPS | 取大值 |
#mermaid-svg-Pn7KFVCMdJp9w54g{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Pn7KFVCMdJp9w54g .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Pn7KFVCMdJp9w54g .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Pn7KFVCMdJp9w54g .error-icon{fill:#552222;}#mermaid-svg-Pn7KFVCMdJp9w54g .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Pn7KFVCMdJp9w54g .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Pn7KFVCMdJp9w54g .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Pn7KFVCMdJp9w54g .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Pn7KFVCMdJp9w54g .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Pn7KFVCMdJp9w54g .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Pn7KFVCMdJp9w54g .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Pn7KFVCMdJp9w54g .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Pn7KFVCMdJp9w54g .marker.cross{stroke:#333333;}#mermaid-svg-Pn7KFVCMdJp9w54g svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Pn7KFVCMdJp9w54g p{margin:0;}#mermaid-svg-Pn7KFVCMdJp9w54g .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Pn7KFVCMdJp9w54g .cluster-label text{fill:#333;}#mermaid-svg-Pn7KFVCMdJp9w54g .cluster-label span{color:#333;}#mermaid-svg-Pn7KFVCMdJp9w54g .cluster-label span p{background-color:transparent;}#mermaid-svg-Pn7KFVCMdJp9w54g .label text,#mermaid-svg-Pn7KFVCMdJp9w54g span{fill:#333;color:#333;}#mermaid-svg-Pn7KFVCMdJp9w54g .node rect,#mermaid-svg-Pn7KFVCMdJp9w54g .node circle,#mermaid-svg-Pn7KFVCMdJp9w54g .node ellipse,#mermaid-svg-Pn7KFVCMdJp9w54g .node polygon,#mermaid-svg-Pn7KFVCMdJp9w54g .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Pn7KFVCMdJp9w54g .rough-node .label text,#mermaid-svg-Pn7KFVCMdJp9w54g .node .label text,#mermaid-svg-Pn7KFVCMdJp9w54g .image-shape .label,#mermaid-svg-Pn7KFVCMdJp9w54g .icon-shape .label{text-anchor:middle;}#mermaid-svg-Pn7KFVCMdJp9w54g .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Pn7KFVCMdJp9w54g .rough-node .label,#mermaid-svg-Pn7KFVCMdJp9w54g .node .label,#mermaid-svg-Pn7KFVCMdJp9w54g .image-shape .label,#mermaid-svg-Pn7KFVCMdJp9w54g .icon-shape .label{text-align:center;}#mermaid-svg-Pn7KFVCMdJp9w54g .node.clickable{cursor:pointer;}#mermaid-svg-Pn7KFVCMdJp9w54g .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Pn7KFVCMdJp9w54g .arrowheadPath{fill:#333333;}#mermaid-svg-Pn7KFVCMdJp9w54g .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Pn7KFVCMdJp9w54g .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Pn7KFVCMdJp9w54g .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Pn7KFVCMdJp9w54g .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Pn7KFVCMdJp9w54g .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Pn7KFVCMdJp9w54g .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Pn7KFVCMdJp9w54g .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Pn7KFVCMdJp9w54g .cluster text{fill:#333;}#mermaid-svg-Pn7KFVCMdJp9w54g .cluster span{color:#333;}#mermaid-svg-Pn7KFVCMdJp9w54g div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Pn7KFVCMdJp9w54g .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Pn7KFVCMdJp9w54g rect.text{fill:none;stroke-width:0;}#mermaid-svg-Pn7KFVCMdJp9w54g .icon-shape,#mermaid-svg-Pn7KFVCMdJp9w54g .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Pn7KFVCMdJp9w54g .icon-shape p,#mermaid-svg-Pn7KFVCMdJp9w54g .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Pn7KFVCMdJp9w54g .icon-shape .label rect,#mermaid-svg-Pn7KFVCMdJp9w54g .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Pn7KFVCMdJp9w54g .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Pn7KFVCMdJp9w54g .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Pn7KFVCMdJp9w54g :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} AddRateLimited(item)
ItemExponentialFailureRateLimiter
When(item) → 单 item 延迟
BucketRateLimiter
When(item) → 全局延迟
MaxOfRateLimiter
max(exp_delay, bucket_delay)
最终延迟 = max(两者)
设计意图:
- 指数退避:防止单个 item 频繁重试(热循环)
- 令牌桶:防止大量 item 同时重试压垮 API Server
- 取最大值:保证两层限流都生效
6.5 ItemFastSlowRateLimiter --- 快慢限流器
go
type TypedItemFastSlowRateLimiter[T comparable] struct {
failuresLock sync.Mutex
failures map[T]int
maxFastAttempts int // 快速重试次数阈值
fastDelay time.Duration // 快速阶段延迟
slowDelay time.Duration // 慢速阶段延迟
}
func (r *TypedItemFastSlowRateLimiter[T]) When(item T) time.Duration {
r.failuresLock.Lock()
defer r.failuresLock.Unlock()
r.failures[item] = r.failures[item] + 1
if r.failures[item] <= r.maxFastAttempts {
return r.fastDelay
// 前 N 次用短延迟
}
return r.slowDelay
// N 次之后用长延迟
}
快慢退避曲线:
#mermaid-svg-skhVmsMKVil20sUj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-skhVmsMKVil20sUj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-skhVmsMKVil20sUj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-skhVmsMKVil20sUj .error-icon{fill:#552222;}#mermaid-svg-skhVmsMKVil20sUj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-skhVmsMKVil20sUj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-skhVmsMKVil20sUj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-skhVmsMKVil20sUj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-skhVmsMKVil20sUj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-skhVmsMKVil20sUj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-skhVmsMKVil20sUj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-skhVmsMKVil20sUj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-skhVmsMKVil20sUj .marker.cross{stroke:#333333;}#mermaid-svg-skhVmsMKVil20sUj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-skhVmsMKVil20sUj p{margin:0;}#mermaid-svg-skhVmsMKVil20sUj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-skhVmsMKVil20sUj .cluster-label text{fill:#333;}#mermaid-svg-skhVmsMKVil20sUj .cluster-label span{color:#333;}#mermaid-svg-skhVmsMKVil20sUj .cluster-label span p{background-color:transparent;}#mermaid-svg-skhVmsMKVil20sUj .label text,#mermaid-svg-skhVmsMKVil20sUj span{fill:#333;color:#333;}#mermaid-svg-skhVmsMKVil20sUj .node rect,#mermaid-svg-skhVmsMKVil20sUj .node circle,#mermaid-svg-skhVmsMKVil20sUj .node ellipse,#mermaid-svg-skhVmsMKVil20sUj .node polygon,#mermaid-svg-skhVmsMKVil20sUj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-skhVmsMKVil20sUj .rough-node .label text,#mermaid-svg-skhVmsMKVil20sUj .node .label text,#mermaid-svg-skhVmsMKVil20sUj .image-shape .label,#mermaid-svg-skhVmsMKVil20sUj .icon-shape .label{text-anchor:middle;}#mermaid-svg-skhVmsMKVil20sUj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-skhVmsMKVil20sUj .rough-node .label,#mermaid-svg-skhVmsMKVil20sUj .node .label,#mermaid-svg-skhVmsMKVil20sUj .image-shape .label,#mermaid-svg-skhVmsMKVil20sUj .icon-shape .label{text-align:center;}#mermaid-svg-skhVmsMKVil20sUj .node.clickable{cursor:pointer;}#mermaid-svg-skhVmsMKVil20sUj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-skhVmsMKVil20sUj .arrowheadPath{fill:#333333;}#mermaid-svg-skhVmsMKVil20sUj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-skhVmsMKVil20sUj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-skhVmsMKVil20sUj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-skhVmsMKVil20sUj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-skhVmsMKVil20sUj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-skhVmsMKVil20sUj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-skhVmsMKVil20sUj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-skhVmsMKVil20sUj .cluster text{fill:#333;}#mermaid-svg-skhVmsMKVil20sUj .cluster span{color:#333;}#mermaid-svg-skhVmsMKVil20sUj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-skhVmsMKVil20sUj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-skhVmsMKVil20sUj rect.text{fill:none;stroke-width:0;}#mermaid-svg-skhVmsMKVil20sUj .icon-shape,#mermaid-svg-skhVmsMKVil20sUj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-skhVmsMKVil20sUj .icon-shape p,#mermaid-svg-skhVmsMKVil20sUj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-skhVmsMKVil20sUj .icon-shape .label rect,#mermaid-svg-skhVmsMKVil20sUj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-skhVmsMKVil20sUj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-skhVmsMKVil20sUj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-skhVmsMKVil20sUj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Slow Phase (> maxFastAttempts)
Fast Phase (1~maxFastAttempts)
attempt 1: fastDelay
attempt 2: fastDelay
attempt N: fastDelay
attempt N+1: slowDelay
attempt N+2: slowDelay
attempt ∞: slowDelay
6.6 WithMaxWaitRateLimiter --- 最大等待限流器
go
type TypedWithMaxWaitRateLimiter[T comparable] struct {
limiter TypedRateLimiter[T] // 被包装的限流器
maxDelay time.Duration // 最大等待时间
}
func (w TypedWithMaxWaitRateLimiter[T]) When(item T) time.Duration {
delay := w.limiter.When(item)
if delay > w.maxDelay {
return w.maxDelay
// 超过最大等待 → 截断
}
return delay
}
用途:给指数退避限流器加一个硬上限,防止极端情况下等待过长。
6.7 限流器组合关系
#mermaid-svg-KQTflOzVGTpy4MJ2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KQTflOzVGTpy4MJ2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KQTflOzVGTpy4MJ2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KQTflOzVGTpy4MJ2 .error-icon{fill:#552222;}#mermaid-svg-KQTflOzVGTpy4MJ2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KQTflOzVGTpy4MJ2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KQTflOzVGTpy4MJ2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KQTflOzVGTpy4MJ2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KQTflOzVGTpy4MJ2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KQTflOzVGTpy4MJ2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KQTflOzVGTpy4MJ2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KQTflOzVGTpy4MJ2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KQTflOzVGTpy4MJ2 .marker.cross{stroke:#333333;}#mermaid-svg-KQTflOzVGTpy4MJ2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KQTflOzVGTpy4MJ2 p{margin:0;}#mermaid-svg-KQTflOzVGTpy4MJ2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KQTflOzVGTpy4MJ2 .cluster-label text{fill:#333;}#mermaid-svg-KQTflOzVGTpy4MJ2 .cluster-label span{color:#333;}#mermaid-svg-KQTflOzVGTpy4MJ2 .cluster-label span p{background-color:transparent;}#mermaid-svg-KQTflOzVGTpy4MJ2 .label text,#mermaid-svg-KQTflOzVGTpy4MJ2 span{fill:#333;color:#333;}#mermaid-svg-KQTflOzVGTpy4MJ2 .node rect,#mermaid-svg-KQTflOzVGTpy4MJ2 .node circle,#mermaid-svg-KQTflOzVGTpy4MJ2 .node ellipse,#mermaid-svg-KQTflOzVGTpy4MJ2 .node polygon,#mermaid-svg-KQTflOzVGTpy4MJ2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KQTflOzVGTpy4MJ2 .rough-node .label text,#mermaid-svg-KQTflOzVGTpy4MJ2 .node .label text,#mermaid-svg-KQTflOzVGTpy4MJ2 .image-shape .label,#mermaid-svg-KQTflOzVGTpy4MJ2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-KQTflOzVGTpy4MJ2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KQTflOzVGTpy4MJ2 .rough-node .label,#mermaid-svg-KQTflOzVGTpy4MJ2 .node .label,#mermaid-svg-KQTflOzVGTpy4MJ2 .image-shape .label,#mermaid-svg-KQTflOzVGTpy4MJ2 .icon-shape .label{text-align:center;}#mermaid-svg-KQTflOzVGTpy4MJ2 .node.clickable{cursor:pointer;}#mermaid-svg-KQTflOzVGTpy4MJ2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KQTflOzVGTpy4MJ2 .arrowheadPath{fill:#333333;}#mermaid-svg-KQTflOzVGTpy4MJ2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KQTflOzVGTpy4MJ2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KQTflOzVGTpy4MJ2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KQTflOzVGTpy4MJ2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KQTflOzVGTpy4MJ2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KQTflOzVGTpy4MJ2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KQTflOzVGTpy4MJ2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KQTflOzVGTpy4MJ2 .cluster text{fill:#333;}#mermaid-svg-KQTflOzVGTpy4MJ2 .cluster span{color:#333;}#mermaid-svg-KQTflOzVGTpy4MJ2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KQTflOzVGTpy4MJ2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KQTflOzVGTpy4MJ2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-KQTflOzVGTpy4MJ2 .icon-shape,#mermaid-svg-KQTflOzVGTpy4MJ2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KQTflOzVGTpy4MJ2 .icon-shape p,#mermaid-svg-KQTflOzVGTpy4MJ2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KQTflOzVGTpy4MJ2 .icon-shape .label rect,#mermaid-svg-KQTflOzVGTpy4MJ2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KQTflOzVGTpy4MJ2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KQTflOzVGTpy4MJ2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KQTflOzVGTpy4MJ2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} DefaultControllerRateLimiter
MaxOfRateLimiter
ItemExponentialFailureRateLimiter
5ms base, 1000s max
BucketRateLimiter
10 QPS, 100 burst
ItemFastSlowRateLimiter
fastDelay, slowDelay, maxFast
WithMaxWaitRateLimiter
limiter + maxDelay
被包装的限流器
七、指标系统 (metrics.go)
7.1 指标接口
go
type queueMetrics[T comparable] interface {
add(item T) // Add 时调用:adds++ + depth++
get(item T) // Get 时调用:depth-- + 记录 latency
done(item T) // Done 时调用:记录 workDuration
updateUnfinishedWork() // 定期更新:unfinishedWorkSeconds + longestRunningProcessor
}
type MetricsProvider interface {
NewDepthMetric(name string) GaugeMetric
NewAddsMetric(name string) CounterMetric
NewLatencyMetric(name string) HistogramMetric
NewWorkDurationMetric(name string) HistogramMetric
NewUnfinishedWorkSecondsMetric(name string) SettableGaugeMetric
NewLongestRunningProcessorSecondsMetric(name string) SettableGaugeMetric
NewRetriesMetric(name string) CounterMetric
}
7.2 defaultQueueMetrics 指标记录详解
go
type defaultQueueMetrics[T comparable] struct {
clock clock.Clock
depth GaugeMetric // 当前队列深度
adds CounterMetric // 总入队次数
latency HistogramMetric // 入队到出队的延迟分布
workDuration HistogramMetric // 处理耗时分布
addTimes map[T]time.Time // item 入队时间
processingStartTimes map[T]time.Time // item 开始处理时间
unfinishedWorkSeconds SettableGaugeMetric // 当前所有 worker 累计工作时间
longestRunningProcessor SettableGaugeMetric // 最长运行时间
}
指标采集时机
defaultQueueMetrics Queue 用户代码 defaultQueueMetrics Queue 用户代码 #mermaid-svg-b0cZQeFRbL5ZtVlJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-b0cZQeFRbL5ZtVlJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .error-icon{fill:#552222;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .marker.cross{stroke:#333333;}#mermaid-svg-b0cZQeFRbL5ZtVlJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-b0cZQeFRbL5ZtVlJ p{margin:0;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-b0cZQeFRbL5ZtVlJ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-b0cZQeFRbL5ZtVlJ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-b0cZQeFRbL5ZtVlJ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .sequenceNumber{fill:white;}#mermaid-svg-b0cZQeFRbL5ZtVlJ #sequencenumber{fill:#333;}#mermaid-svg-b0cZQeFRbL5ZtVlJ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .messageText{fill:#333;stroke:none;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .labelText,#mermaid-svg-b0cZQeFRbL5ZtVlJ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .loopText,#mermaid-svg-b0cZQeFRbL5ZtVlJ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-b0cZQeFRbL5ZtVlJ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .noteText,#mermaid-svg-b0cZQeFRbL5ZtVlJ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .actorPopupMenu{position:absolute;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-b0cZQeFRbL5ZtVlJ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-b0cZQeFRbL5ZtVlJ .actor-man circle,#mermaid-svg-b0cZQeFRbL5ZtVlJ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-b0cZQeFRbL5ZtVlJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} adds.Inc() + depth.Inc()addTimesitem = now depth.Dec()latency.Observe(now - addTimesitem)processingStartTimesitem = nowdelete(addTimes, item) workDuration.Observe(now - processingStartTimesitem)delete(processingStartTimes, item) 每 500ms: updateUnfinishedWork()unfinishedWorkSeconds = Σ(now - processingStartTimesi)longestRunningProcessor = max(now - processingStartTimesi) Add(item)add(item)Get() → itemget(item)Done(item)done(item)
7.3 retryMetrics --- 重试指标
go
type retryMetrics interface {
retry()
}
type defaultRetryMetrics struct {
retries CounterMetric
}
func (m *defaultRetryMetrics) retry() {
if m == nil { return }
m.retries.Inc()
// 每次 AddAfter 调用都计为一次 retry
}
7.4 全局 MetricsProvider
go
var globalMetricsProvider MetricsProvider = noopMetricsProvider{}
// 默认无指标(no-op)
var setGlobalMetricsProviderOnce sync.Once
func SetProvider(metricsProvider MetricsProvider) {
setGlobalMetricsProviderOnce.Do(func() {
globalMetricsProvider = metricsProvider
// 只生效一次,通常在程序启动时调用
})
}
func newQueueMetrics[T comparable](mp MetricsProvider, name string, clock clock.Clock) queueMetrics[T] {
if len(name) == 0 || mp == (noopMetricsProvider{}) {
return noMetrics[T]{}
// 无名队列或无 provider → 不采集指标
// 也不启动 updateUnfinishedWorkLoop
}
return &defaultQueueMetrics[T]{
clock: clock,
depth: mp.NewDepthMetric(name),
adds: mp.NewAddsMetric(name),
latency: mp.NewLatencyMetric(name),
workDuration: mp.NewWorkDurationMetric(name),
unfinishedWorkSeconds: mp.NewUnfinishedWorkSecondsMetric(name),
longestRunningProcessor: mp.NewLongestRunningProcessorSecondsMetric(name),
addTimes: map[T]time.Time{},
processingStartTimes: map[T]time.Time{},
}
}
八、并行化工具 (parallelizer.go)
8.1 ParallelizeUntil --- 并行执行框架
go
type DoWorkPieceFunc func(piece int)
func ParallelizeUntil(ctx context.Context, workers, pieces int, doWorkPiece DoWorkPieceFunc, opts ...Options) {
if pieces == 0 {
return
}
// ─── 解析选项 ───
o := options{}
for _, opt := range opts {
opt(&o)
}
chunkSize := o.chunkSize
if chunkSize < 1 {
chunkSize = 1
// 默认每个 piece 独立处理
}
// ─── 分块 ───
chunks := ceilDiv(pieces, chunkSize)
// 将 pieces 分成 chunks 个块
// ─── 创建工作通道 ───
toProcess := make(chan int, chunks)
for i := 0; i < chunks; i++ {
toProcess <- i // 放入块编号
}
close(toProcess) // 关闭通道,worker 通过 range 接收
var stop <-chan struct{}
if ctx != nil {
stop = ctx.Done()
}
// ─── 限制 worker 数量 ───
if chunks < workers {
workers = chunks
// 块数少于 worker 数 → 减少 worker 数
}
// ─── 启动 worker ───
wg := sync.WaitGroup{}
wg.Add(workers)
for i := 0; i < workers; i++ {
go func() {
defer utilruntime.HandleCrashWithContext(ctx)
defer wg.Done()
for chunk := range toProcess {
// 计算当前块对应的 piece 范围
start := chunk * chunkSize
end := start + chunkSize
if end > pieces {
end = pieces
}
for p := start; p < end; p++ {
select {
case <-stop:
return
// ctx 取消 → 退出
default:
doWorkPiece(p)
// 处理单个 piece
}
}
}
}()
}
wg.Wait()
// 等待所有 worker 完成
}
ParallelizeUntil 的工作模式:
#mermaid-svg-87FKQUW648q26M29{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-87FKQUW648q26M29 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-87FKQUW648q26M29 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-87FKQUW648q26M29 .error-icon{fill:#552222;}#mermaid-svg-87FKQUW648q26M29 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-87FKQUW648q26M29 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-87FKQUW648q26M29 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-87FKQUW648q26M29 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-87FKQUW648q26M29 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-87FKQUW648q26M29 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-87FKQUW648q26M29 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-87FKQUW648q26M29 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-87FKQUW648q26M29 .marker.cross{stroke:#333333;}#mermaid-svg-87FKQUW648q26M29 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-87FKQUW648q26M29 p{margin:0;}#mermaid-svg-87FKQUW648q26M29 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-87FKQUW648q26M29 .cluster-label text{fill:#333;}#mermaid-svg-87FKQUW648q26M29 .cluster-label span{color:#333;}#mermaid-svg-87FKQUW648q26M29 .cluster-label span p{background-color:transparent;}#mermaid-svg-87FKQUW648q26M29 .label text,#mermaid-svg-87FKQUW648q26M29 span{fill:#333;color:#333;}#mermaid-svg-87FKQUW648q26M29 .node rect,#mermaid-svg-87FKQUW648q26M29 .node circle,#mermaid-svg-87FKQUW648q26M29 .node ellipse,#mermaid-svg-87FKQUW648q26M29 .node polygon,#mermaid-svg-87FKQUW648q26M29 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-87FKQUW648q26M29 .rough-node .label text,#mermaid-svg-87FKQUW648q26M29 .node .label text,#mermaid-svg-87FKQUW648q26M29 .image-shape .label,#mermaid-svg-87FKQUW648q26M29 .icon-shape .label{text-anchor:middle;}#mermaid-svg-87FKQUW648q26M29 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-87FKQUW648q26M29 .rough-node .label,#mermaid-svg-87FKQUW648q26M29 .node .label,#mermaid-svg-87FKQUW648q26M29 .image-shape .label,#mermaid-svg-87FKQUW648q26M29 .icon-shape .label{text-align:center;}#mermaid-svg-87FKQUW648q26M29 .node.clickable{cursor:pointer;}#mermaid-svg-87FKQUW648q26M29 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-87FKQUW648q26M29 .arrowheadPath{fill:#333333;}#mermaid-svg-87FKQUW648q26M29 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-87FKQUW648q26M29 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-87FKQUW648q26M29 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-87FKQUW648q26M29 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-87FKQUW648q26M29 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-87FKQUW648q26M29 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-87FKQUW648q26M29 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-87FKQUW648q26M29 .cluster text{fill:#333;}#mermaid-svg-87FKQUW648q26M29 .cluster span{color:#333;}#mermaid-svg-87FKQUW648q26M29 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-87FKQUW648q26M29 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-87FKQUW648q26M29 rect.text{fill:none;stroke-width:0;}#mermaid-svg-87FKQUW648q26M29 .icon-shape,#mermaid-svg-87FKQUW648q26M29 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-87FKQUW648q26M29 .icon-shape p,#mermaid-svg-87FKQUW648q26M29 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-87FKQUW648q26M29 .icon-shape .label rect,#mermaid-svg-87FKQUW648q26M29 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-87FKQUW648q26M29 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-87FKQUW648q26M29 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-87FKQUW648q26M29 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ParallelizeUntil(ctx, workers=4, pieces=10, fn)
分块: chunkSize=1, chunks=10
toProcess chan: 0,1,2,3,4,5,6,7,8,9
Worker 1
处理 piece 0,1,2
Worker 2
处理 piece 3,4,5
Worker 3
处理 piece 6,7
Worker 4
处理 piece 8,9
wg.Wait()
九、完整生命周期与设计模式
9.1 WorkQueue 完整生命周期
#mermaid-svg-FgtKcwyEFfMwaYIv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FgtKcwyEFfMwaYIv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FgtKcwyEFfMwaYIv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FgtKcwyEFfMwaYIv .error-icon{fill:#552222;}#mermaid-svg-FgtKcwyEFfMwaYIv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FgtKcwyEFfMwaYIv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FgtKcwyEFfMwaYIv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FgtKcwyEFfMwaYIv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FgtKcwyEFfMwaYIv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FgtKcwyEFfMwaYIv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FgtKcwyEFfMwaYIv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FgtKcwyEFfMwaYIv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FgtKcwyEFfMwaYIv .marker.cross{stroke:#333333;}#mermaid-svg-FgtKcwyEFfMwaYIv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FgtKcwyEFfMwaYIv p{margin:0;}#mermaid-svg-FgtKcwyEFfMwaYIv defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-FgtKcwyEFfMwaYIv g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-FgtKcwyEFfMwaYIv g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-FgtKcwyEFfMwaYIv g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-FgtKcwyEFfMwaYIv g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-FgtKcwyEFfMwaYIv g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-FgtKcwyEFfMwaYIv .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-FgtKcwyEFfMwaYIv .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-FgtKcwyEFfMwaYIv .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-FgtKcwyEFfMwaYIv .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-FgtKcwyEFfMwaYIv .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-FgtKcwyEFfMwaYIv .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-FgtKcwyEFfMwaYIv .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-FgtKcwyEFfMwaYIv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FgtKcwyEFfMwaYIv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FgtKcwyEFfMwaYIv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FgtKcwyEFfMwaYIv .edgeLabel .label text{fill:#333;}#mermaid-svg-FgtKcwyEFfMwaYIv .label div .edgeLabel{color:#333;}#mermaid-svg-FgtKcwyEFfMwaYIv .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-FgtKcwyEFfMwaYIv .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-FgtKcwyEFfMwaYIv .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-FgtKcwyEFfMwaYIv .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-FgtKcwyEFfMwaYIv .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-FgtKcwyEFfMwaYIv .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FgtKcwyEFfMwaYIv .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FgtKcwyEFfMwaYIv #statediagram-barbEnd{fill:#333333;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FgtKcwyEFfMwaYIv .cluster-label,#mermaid-svg-FgtKcwyEFfMwaYIv .nodeLabel{color:#131300;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-FgtKcwyEFfMwaYIv .note-edge{stroke-dasharray:5;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-note text{fill:black;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram-note .nodeLabel{color:black;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagram .edgeLabel{color:red;}#mermaid-svg-FgtKcwyEFfMwaYIv #dependencyStart,#mermaid-svg-FgtKcwyEFfMwaYIv #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-FgtKcwyEFfMwaYIv .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FgtKcwyEFfMwaYIv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} NewTypedWithConfig()
Add() / Get()
ShutDownWithDrain()
ShutDown()
all items processed
all workers exit
Created
Running
queue empty
Get() returns item
Done() + queue empty
Done() + dirty.Has → re-enqueue
Done() + !dirty.Has
Waiting
Processing
Draining
processing.Len() > 0
processing.Len() == 0
WaitProcessing
Done1
ShuttingDown
shuttingDown=true
queue empty → Get returns shutdown=true
StopAccept
DrainQueue
9.2 用户 Controller 标准使用模式
go
// 创建限流队列
queue := workqueue.NewTypedRateLimitingQueue(
workqueue.DefaultTypedControllerRateLimiter[string](),
)
// 注册 EventHandler
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
key, _ := cache.MetaNamespaceKeyFunc(obj)
queue.Add(key) // 入队
},
UpdateFunc: func(old, new interface{}) {
key, _ := cache.MetaNamespaceKeyFunc(new)
queue.Add(key) // 入队(去重)
},
DeleteFunc: func(obj interface{}) {
key, _ := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
queue.Add(key) // 入队
},
})
// 启动 Worker
for i := 0; i < workers; i++ {
go func() {
for {
key, shutdown := queue.Get()
if shutdown {
return // 队列关闭 → 退出
}
err := reconcile(key) // 调谐逻辑
if err != nil {
queue.AddRateLimited(key) // 失败 → 限流重试
} else {
queue.Forget(key) // 成功 → 清除计数
}
queue.Done(key) // 标记完成
}
}()
}
9.3 Add 的完整决策树
#mermaid-svg-Uu3UWXb9DTnqMOZ4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .error-icon{fill:#552222;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .marker.cross{stroke:#333333;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 p{margin:0;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .cluster-label text{fill:#333;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .cluster-label span{color:#333;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .cluster-label span p{background-color:transparent;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .label text,#mermaid-svg-Uu3UWXb9DTnqMOZ4 span{fill:#333;color:#333;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .node rect,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .node circle,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .node ellipse,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .node polygon,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .rough-node .label text,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .node .label text,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .image-shape .label,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .rough-node .label,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .node .label,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .image-shape .label,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .icon-shape .label{text-align:center;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .node.clickable{cursor:pointer;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .arrowheadPath{fill:#333333;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .cluster text{fill:#333;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .cluster span{color:#333;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .icon-shape,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .icon-shape p,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .icon-shape .label rect,#mermaid-svg-Uu3UWXb9DTnqMOZ4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Uu3UWXb9DTnqMOZ4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Yes
No
不在 processing
在 processing
Yes
No
Add(item)
cond.L.Lock()
shuttingDown?
dirty.Has(item)?
processing.Has(item)?
queue.Touch(item) (更新优先级)
return: item 在 dirty+processing
Done 时会重新入队
metrics.add(item)
dirty.Insert(item)
processing.Has(item)?
queue.Push(item) + cond.Signal()
return: 只加 dirty, Done 时重新入队
return: 静默忽略
9.4 Get 与 Done 的状态转移详解
Get 流程
#mermaid-svg-610L55p1fi0G0Z6F{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-610L55p1fi0G0Z6F .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-610L55p1fi0G0Z6F .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-610L55p1fi0G0Z6F .error-icon{fill:#552222;}#mermaid-svg-610L55p1fi0G0Z6F .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-610L55p1fi0G0Z6F .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-610L55p1fi0G0Z6F .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-610L55p1fi0G0Z6F .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-610L55p1fi0G0Z6F .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-610L55p1fi0G0Z6F .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-610L55p1fi0G0Z6F .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-610L55p1fi0G0Z6F .marker{fill:#333333;stroke:#333333;}#mermaid-svg-610L55p1fi0G0Z6F .marker.cross{stroke:#333333;}#mermaid-svg-610L55p1fi0G0Z6F svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-610L55p1fi0G0Z6F p{margin:0;}#mermaid-svg-610L55p1fi0G0Z6F .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-610L55p1fi0G0Z6F .cluster-label text{fill:#333;}#mermaid-svg-610L55p1fi0G0Z6F .cluster-label span{color:#333;}#mermaid-svg-610L55p1fi0G0Z6F .cluster-label span p{background-color:transparent;}#mermaid-svg-610L55p1fi0G0Z6F .label text,#mermaid-svg-610L55p1fi0G0Z6F span{fill:#333;color:#333;}#mermaid-svg-610L55p1fi0G0Z6F .node rect,#mermaid-svg-610L55p1fi0G0Z6F .node circle,#mermaid-svg-610L55p1fi0G0Z6F .node ellipse,#mermaid-svg-610L55p1fi0G0Z6F .node polygon,#mermaid-svg-610L55p1fi0G0Z6F .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-610L55p1fi0G0Z6F .rough-node .label text,#mermaid-svg-610L55p1fi0G0Z6F .node .label text,#mermaid-svg-610L55p1fi0G0Z6F .image-shape .label,#mermaid-svg-610L55p1fi0G0Z6F .icon-shape .label{text-anchor:middle;}#mermaid-svg-610L55p1fi0G0Z6F .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-610L55p1fi0G0Z6F .rough-node .label,#mermaid-svg-610L55p1fi0G0Z6F .node .label,#mermaid-svg-610L55p1fi0G0Z6F .image-shape .label,#mermaid-svg-610L55p1fi0G0Z6F .icon-shape .label{text-align:center;}#mermaid-svg-610L55p1fi0G0Z6F .node.clickable{cursor:pointer;}#mermaid-svg-610L55p1fi0G0Z6F .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-610L55p1fi0G0Z6F .arrowheadPath{fill:#333333;}#mermaid-svg-610L55p1fi0G0Z6F .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-610L55p1fi0G0Z6F .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-610L55p1fi0G0Z6F .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-610L55p1fi0G0Z6F .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-610L55p1fi0G0Z6F .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-610L55p1fi0G0Z6F .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-610L55p1fi0G0Z6F .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-610L55p1fi0G0Z6F .cluster text{fill:#333;}#mermaid-svg-610L55p1fi0G0Z6F .cluster span{color:#333;}#mermaid-svg-610L55p1fi0G0Z6F div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-610L55p1fi0G0Z6F .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-610L55p1fi0G0Z6F rect.text{fill:none;stroke-width:0;}#mermaid-svg-610L55p1fi0G0Z6F .icon-shape,#mermaid-svg-610L55p1fi0G0Z6F .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-610L55p1fi0G0Z6F .icon-shape p,#mermaid-svg-610L55p1fi0G0Z6F .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-610L55p1fi0G0Z6F .icon-shape .label rect,#mermaid-svg-610L55p1fi0G0Z6F .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-610L55p1fi0G0Z6F .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-610L55p1fi0G0Z6F .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-610L55p1fi0G0Z6F :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Yes (shuttingDown)
No
Get()
cond.L.Lock()
queue.Len()==0 && !shuttingDown?
cond.Wait() (阻塞)
queue.Len()==0?
queue.Pop() → item
metrics.get(item)
processing.Insert(item)
dirty.Delete(item)
return (item, false)
return (零值, true)
Done 流程
#mermaid-svg-daTJmbg5C32rHNZe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-daTJmbg5C32rHNZe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-daTJmbg5C32rHNZe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-daTJmbg5C32rHNZe .error-icon{fill:#552222;}#mermaid-svg-daTJmbg5C32rHNZe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-daTJmbg5C32rHNZe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-daTJmbg5C32rHNZe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-daTJmbg5C32rHNZe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-daTJmbg5C32rHNZe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-daTJmbg5C32rHNZe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-daTJmbg5C32rHNZe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-daTJmbg5C32rHNZe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-daTJmbg5C32rHNZe .marker.cross{stroke:#333333;}#mermaid-svg-daTJmbg5C32rHNZe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-daTJmbg5C32rHNZe p{margin:0;}#mermaid-svg-daTJmbg5C32rHNZe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-daTJmbg5C32rHNZe .cluster-label text{fill:#333;}#mermaid-svg-daTJmbg5C32rHNZe .cluster-label span{color:#333;}#mermaid-svg-daTJmbg5C32rHNZe .cluster-label span p{background-color:transparent;}#mermaid-svg-daTJmbg5C32rHNZe .label text,#mermaid-svg-daTJmbg5C32rHNZe span{fill:#333;color:#333;}#mermaid-svg-daTJmbg5C32rHNZe .node rect,#mermaid-svg-daTJmbg5C32rHNZe .node circle,#mermaid-svg-daTJmbg5C32rHNZe .node ellipse,#mermaid-svg-daTJmbg5C32rHNZe .node polygon,#mermaid-svg-daTJmbg5C32rHNZe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-daTJmbg5C32rHNZe .rough-node .label text,#mermaid-svg-daTJmbg5C32rHNZe .node .label text,#mermaid-svg-daTJmbg5C32rHNZe .image-shape .label,#mermaid-svg-daTJmbg5C32rHNZe .icon-shape .label{text-anchor:middle;}#mermaid-svg-daTJmbg5C32rHNZe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-daTJmbg5C32rHNZe .rough-node .label,#mermaid-svg-daTJmbg5C32rHNZe .node .label,#mermaid-svg-daTJmbg5C32rHNZe .image-shape .label,#mermaid-svg-daTJmbg5C32rHNZe .icon-shape .label{text-align:center;}#mermaid-svg-daTJmbg5C32rHNZe .node.clickable{cursor:pointer;}#mermaid-svg-daTJmbg5C32rHNZe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-daTJmbg5C32rHNZe .arrowheadPath{fill:#333333;}#mermaid-svg-daTJmbg5C32rHNZe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-daTJmbg5C32rHNZe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-daTJmbg5C32rHNZe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-daTJmbg5C32rHNZe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-daTJmbg5C32rHNZe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-daTJmbg5C32rHNZe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-daTJmbg5C32rHNZe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-daTJmbg5C32rHNZe .cluster text{fill:#333;}#mermaid-svg-daTJmbg5C32rHNZe .cluster span{color:#333;}#mermaid-svg-daTJmbg5C32rHNZe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-daTJmbg5C32rHNZe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-daTJmbg5C32rHNZe rect.text{fill:none;stroke-width:0;}#mermaid-svg-daTJmbg5C32rHNZe .icon-shape,#mermaid-svg-daTJmbg5C32rHNZe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-daTJmbg5C32rHNZe .icon-shape p,#mermaid-svg-daTJmbg5C32rHNZe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-daTJmbg5C32rHNZe .icon-shape .label rect,#mermaid-svg-daTJmbg5C32rHNZe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-daTJmbg5C32rHNZe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-daTJmbg5C32rHNZe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-daTJmbg5C32rHNZe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Yes
No
Done(item)
cond.L.Lock()
metrics.done(item)
processing.Delete(item)
dirty.Has(item)?
queue.Push(item) + cond.Signal()
processing.Len()==0?
cond.Signal()
return
9.5 waitingLoop 的定时器管理
waitingLoop 使用四个事件源多路复用:
- stopCh:关闭信号 → 退出
- heartbeat.C():10s 心跳 → 重新检查堆顶(保险机制)
- nextReadyAt:堆顶条目到期 → 弹出到期条目
- waitingForAddCh:新延迟条目 → insert/Add + 排空通道
heartbeat 的必要性 :即使 nextReadyAtTimer 因为时钟问题未触发,heartbeat 保证最多 10s 就会重新检查堆顶。这是一种防御性编程。
9.6 核心设计模式
| 模式 | WorkQueue 中的体现 |
|---|---|
| 三层装饰器 | RateLimiting → Delaying → Queue,逐层添加能力 |
| 三集合去重 | dirty + queue + processing,保证同 item 不并发处理 |
| 指数退避 | baseDelay×2^failures,防止热循环 |
| 双层限流 | 单 item 退避 + 全局令牌桶,MaxOfRateLimiter 取最大 |
| 条件变量 | sync.Cond 实现阻塞 Get 和优雅关闭 |
| 最小堆调度 | waitForPriorityQueue 实现延迟队列 |
| 去重索引 | waitingEntryByData map 保证堆中同一 data 只有一份 |
| 心跳保险 | 10s heartbeat 防止定时器异常导致永久阻塞 |
| 优雅降级 | AddAfter(duration≤0) → 直接 Add |
| 泛型参数化 | TypedT comparable 替代旧 Interface(any) |
| 可测试时钟 | clock.WithTicker 可注入 FakeClock |
| 指标可插拔 | MetricsProvider 接口,默认 noop |
9.4 关键不变量
- 同 item 不并发:processing 集合保证 Get 出的 item 不会被另一个 Get 取出
- dirty 保证不遗漏:处理期间 Add 的 item 留在 dirty,Done 时重新入队
- FIFO 顺序:queue 是 slice 实现,Pop 从头部取
- 关闭后不接收:shuttingDown=true 后 Add 被静默忽略
- drain 语义:ShutDownWithDrain 等待 processing 清空
- 堆去重:insert 保证同一 data 在堆中只有一份,保留最早 readyAt
- 指数退避有上限:maxDelay (1000s) 截断极端退避值
- 令牌桶限全局:BucketRateLimiter 不区分 item,限制整体 QPS
9.5 源文件索引
| 文件 | 行数 | 核心内容 |
|---|---|---|
queue.go |
370 | TypedT 基础队列:三集合模型、Add/Get/Done/ShutDown |
delaying_queue.go |
369 | delayingType 延迟队列:AddAfter、waitingLoop、优先堆 |
rate_limiting_queue.go |
147 | rateLimitingType 限流队列:AddRateLimited、Forget |
default_rate_limiters.go |
295 | 5 种限流器实现:Exponential/Bucket/MaxOf/FastSlow/WithMaxWait |
metrics.go |
255 | 指标系统:queueMetrics、retryMetrics、MetricsProvider |
parallelizer.go |
101 | ParallelizeUntil 并行执行框架 |
doc.go |
27 | 包文档 |
| 合计 | 1584 | --- |
附录A:WorkQueue 与 DeltaFIFO 的对比
两者都是 client-go 中的队列实现,但职责完全不同:
| 维度 | WorkQueue | DeltaFIFO |
|---|---|---|
| 位置 | 用户 Controller 消费端 | Informer 内部缓冲 |
| 元素类型 | key (string) | Delta{Type, Object} |
| 去重机制 | dirty set + processing set | items map (同 key 合并 Delta) |
| 延迟支持 | ✅ AddAfter + 优先堆 | ❌ 无延迟 |
| 限流支持 | ✅ RateLimiter (指数退避+令牌桶) | ❌ 无限流 |
| 并发模型 | 多 Worker 协程并发 Get | 单协程 processLoop Pop |
| 重入机制 | dirty+processing 重叠 → Done 时重新入队 | Pop 失败 → AddIfNotPresent |
| 关闭语义 | ShutDown / ShutDownWithDrain | Close → Pop 返回 ErrFIFOClosed |
| 通知机制 | sync.Cond (Signal/Broadcast) | 无 (Pop 阻塞在 condition) |
数据流关系:
#mermaid-svg-RHygCinhsVcVe1pO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RHygCinhsVcVe1pO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RHygCinhsVcVe1pO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RHygCinhsVcVe1pO .error-icon{fill:#552222;}#mermaid-svg-RHygCinhsVcVe1pO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RHygCinhsVcVe1pO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RHygCinhsVcVe1pO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RHygCinhsVcVe1pO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RHygCinhsVcVe1pO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RHygCinhsVcVe1pO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RHygCinhsVcVe1pO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RHygCinhsVcVe1pO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RHygCinhsVcVe1pO .marker.cross{stroke:#333333;}#mermaid-svg-RHygCinhsVcVe1pO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RHygCinhsVcVe1pO p{margin:0;}#mermaid-svg-RHygCinhsVcVe1pO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RHygCinhsVcVe1pO .cluster-label text{fill:#333;}#mermaid-svg-RHygCinhsVcVe1pO .cluster-label span{color:#333;}#mermaid-svg-RHygCinhsVcVe1pO .cluster-label span p{background-color:transparent;}#mermaid-svg-RHygCinhsVcVe1pO .label text,#mermaid-svg-RHygCinhsVcVe1pO span{fill:#333;color:#333;}#mermaid-svg-RHygCinhsVcVe1pO .node rect,#mermaid-svg-RHygCinhsVcVe1pO .node circle,#mermaid-svg-RHygCinhsVcVe1pO .node ellipse,#mermaid-svg-RHygCinhsVcVe1pO .node polygon,#mermaid-svg-RHygCinhsVcVe1pO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RHygCinhsVcVe1pO .rough-node .label text,#mermaid-svg-RHygCinhsVcVe1pO .node .label text,#mermaid-svg-RHygCinhsVcVe1pO .image-shape .label,#mermaid-svg-RHygCinhsVcVe1pO .icon-shape .label{text-anchor:middle;}#mermaid-svg-RHygCinhsVcVe1pO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RHygCinhsVcVe1pO .rough-node .label,#mermaid-svg-RHygCinhsVcVe1pO .node .label,#mermaid-svg-RHygCinhsVcVe1pO .image-shape .label,#mermaid-svg-RHygCinhsVcVe1pO .icon-shape .label{text-align:center;}#mermaid-svg-RHygCinhsVcVe1pO .node.clickable{cursor:pointer;}#mermaid-svg-RHygCinhsVcVe1pO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RHygCinhsVcVe1pO .arrowheadPath{fill:#333333;}#mermaid-svg-RHygCinhsVcVe1pO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RHygCinhsVcVe1pO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RHygCinhsVcVe1pO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RHygCinhsVcVe1pO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RHygCinhsVcVe1pO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RHygCinhsVcVe1pO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RHygCinhsVcVe1pO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RHygCinhsVcVe1pO .cluster text{fill:#333;}#mermaid-svg-RHygCinhsVcVe1pO .cluster span{color:#333;}#mermaid-svg-RHygCinhsVcVe1pO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RHygCinhsVcVe1pO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RHygCinhsVcVe1pO rect.text{fill:none;stroke-width:0;}#mermaid-svg-RHygCinhsVcVe1pO .icon-shape,#mermaid-svg-RHygCinhsVcVe1pO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RHygCinhsVcVe1pO .icon-shape p,#mermaid-svg-RHygCinhsVcVe1pO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RHygCinhsVcVe1pO .icon-shape .label rect,#mermaid-svg-RHygCinhsVcVe1pO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RHygCinhsVcVe1pO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RHygCinhsVcVe1pO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RHygCinhsVcVe1pO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户 Controller
Informer 内部
Pop → Deltas
distribute → queue.Add(key)
Get → key
Done(key)
失败: AddRateLimited(key)
成功: Forget(key)
DeltaFIFO
handleDeltas
WorkQueue
Worker
附录B:三集合模型的所有状态组合
| dirty | queue | processing | 含义 | 可能的转移 |
|---|---|---|---|---|
| ✗ | ✗ | ✗ | item 不存在 | Add → 全部✓ |
| ✓ | ✓ | ✗ | 等待处理 | Get → ✓✗✓ |
| ✓ | ✗ | ✓ | 正在处理且被重新标记 | Done → ✓✓✗ |
| ✗ | ✗ | ✓ | 正在处理 | Done → 全部✗ |
| ✓ | ✓ | ✓ | ❌ 不可能 | queue 与 processing 互斥 |
关键不变量 :queue ∩ processing = ∅
附录C:指数退避延迟计算表
DefaultControllerRateLimiter 参数:baseDelay=5ms, maxDelay=1000s
| 失败次数 | 指数退避延迟 | 令牌桶延迟 | 最终延迟 (Max) |
|---|---|---|---|
| 0 | 5ms | ~0ms | 5ms |
| 1 | 10ms | ~0ms | 10ms |
| 2 | 20ms | ~0ms | 20ms |
| 3 | 40ms | ~0ms | 40ms |
| 4 | 80ms | ~0ms | 80ms |
| 5 | 160ms | ~0ms | 160ms |
| 6 | 320ms | ~0ms | 320ms |
| 7 | 640ms | ~0ms | 640ms |
| 8 | 1.28s | ~0ms | 1.28s |
| 9 | 2.56s | ~0ms | 2.56s |
| 10 | 5.12s | ~0ms | 5.12s |
| 12 | 20.48s | ~0ms | 20.48s |
| 15 | 163.84s | ~0ms | 163.84s |
| 18 | ~1310s | ~0ms | 1000s (maxDelay截断) |
| 快速连续(桶空) | 5ms | ~100ms | 100ms (令牌桶主导) |
附录D:WorkQueue 在 Kubernetes Controller 中的完整使用时序
Worker Queue DelayingQueue RateLimiter RateLimitingQueue EventHandler Store handleDeltas DeltaFIFO Reflector kube-apiserver Worker Queue DelayingQueue RateLimiter RateLimitingQueue EventHandler Store handleDeltas DeltaFIFO Reflector kube-apiserver #mermaid-svg-tKIEEZBqebFlJpXl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tKIEEZBqebFlJpXl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tKIEEZBqebFlJpXl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tKIEEZBqebFlJpXl .error-icon{fill:#552222;}#mermaid-svg-tKIEEZBqebFlJpXl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tKIEEZBqebFlJpXl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tKIEEZBqebFlJpXl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tKIEEZBqebFlJpXl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tKIEEZBqebFlJpXl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tKIEEZBqebFlJpXl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tKIEEZBqebFlJpXl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tKIEEZBqebFlJpXl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tKIEEZBqebFlJpXl .marker.cross{stroke:#333333;}#mermaid-svg-tKIEEZBqebFlJpXl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tKIEEZBqebFlJpXl p{margin:0;}#mermaid-svg-tKIEEZBqebFlJpXl .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tKIEEZBqebFlJpXl text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-tKIEEZBqebFlJpXl .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tKIEEZBqebFlJpXl .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-tKIEEZBqebFlJpXl .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-tKIEEZBqebFlJpXl .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-tKIEEZBqebFlJpXl #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-tKIEEZBqebFlJpXl .sequenceNumber{fill:white;}#mermaid-svg-tKIEEZBqebFlJpXl #sequencenumber{fill:#333;}#mermaid-svg-tKIEEZBqebFlJpXl #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-tKIEEZBqebFlJpXl .messageText{fill:#333;stroke:none;}#mermaid-svg-tKIEEZBqebFlJpXl .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tKIEEZBqebFlJpXl .labelText,#mermaid-svg-tKIEEZBqebFlJpXl .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-tKIEEZBqebFlJpXl .loopText,#mermaid-svg-tKIEEZBqebFlJpXl .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-tKIEEZBqebFlJpXl .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tKIEEZBqebFlJpXl .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-tKIEEZBqebFlJpXl .noteText,#mermaid-svg-tKIEEZBqebFlJpXl .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-tKIEEZBqebFlJpXl .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tKIEEZBqebFlJpXl .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tKIEEZBqebFlJpXl .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tKIEEZBqebFlJpXl .actorPopupMenu{position:absolute;}#mermaid-svg-tKIEEZBqebFlJpXl .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-tKIEEZBqebFlJpXl .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tKIEEZBqebFlJpXl .actor-man circle,#mermaid-svg-tKIEEZBqebFlJpXl line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-tKIEEZBqebFlJpXl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 10ms 后... alt成功失败 Watch event (Pod Added)Add(Delta{Added, pod})Pop → DeltasStore.Add(pod)OnAdd(pod, isInInitialList)Add("default/nginx")Get() → "default/nginx"reconcile("default/nginx")Forget("default/nginx")Done("default/nginx")Done("default/nginx")AddRateLimited("default/nginx")When("default/nginx") → 10msAddAfter("default/nginx", 10ms)Add("default/nginx")Get() → "default/nginx" (重试)