Reflector 超深度分析 --- 模块定位、整体结构、接口与依赖
基于
client-go v0.36.1tools/cache/reflector.go(1333行) +reflector_metrics.go(95行) +reflector_data_consistency_detector.go(43行)合计 1471 行源码
一、模块定位
1.1 业务职责
Reflector 是 Kubernetes Informer 架构的数据入口 ,负责从 API Server 拉取资源数据并保持本地 Store 与远端一致。其核心职责可归纳为 "Watch → Store → Notify" 链条的第一环:
| 职责 | 描述 |
|---|---|
| 初始全量同步 | 通过 List 请求获取资源的完整快照,写入 Store |
| 增量变更监听 | 通过 Watch 请求持续监听资源的 Add/Update/Delete 事件 |
| Resync 全量重同步 | 定期将 Store 中的所有对象重新入队,触发 Reconcile |
| ResourceVersion 追踪 | 维护最新观察到的 RV,用于 List/Watch 的断点续传 |
| 流式初始化 (WatchList) | v0.36 新增:通过 WatchList stream 一次性获取初始数据,替代 List |
| 错误恢复与退避 | Watch 断连后自动 backoff 重试,过期 RV 自动降级 |
| 数据一致性校验 | CI 环境下对比 WatchList 与 List 结果是否一致 |
1.2 在系统中的位置
#mermaid-svg-yc25xMS2DcVb6fFt{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-yc25xMS2DcVb6fFt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yc25xMS2DcVb6fFt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yc25xMS2DcVb6fFt .error-icon{fill:#552222;}#mermaid-svg-yc25xMS2DcVb6fFt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yc25xMS2DcVb6fFt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yc25xMS2DcVb6fFt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yc25xMS2DcVb6fFt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yc25xMS2DcVb6fFt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yc25xMS2DcVb6fFt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yc25xMS2DcVb6fFt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yc25xMS2DcVb6fFt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yc25xMS2DcVb6fFt .marker.cross{stroke:#333333;}#mermaid-svg-yc25xMS2DcVb6fFt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yc25xMS2DcVb6fFt p{margin:0;}#mermaid-svg-yc25xMS2DcVb6fFt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yc25xMS2DcVb6fFt .cluster-label text{fill:#333;}#mermaid-svg-yc25xMS2DcVb6fFt .cluster-label span{color:#333;}#mermaid-svg-yc25xMS2DcVb6fFt .cluster-label span p{background-color:transparent;}#mermaid-svg-yc25xMS2DcVb6fFt .label text,#mermaid-svg-yc25xMS2DcVb6fFt span{fill:#333;color:#333;}#mermaid-svg-yc25xMS2DcVb6fFt .node rect,#mermaid-svg-yc25xMS2DcVb6fFt .node circle,#mermaid-svg-yc25xMS2DcVb6fFt .node ellipse,#mermaid-svg-yc25xMS2DcVb6fFt .node polygon,#mermaid-svg-yc25xMS2DcVb6fFt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yc25xMS2DcVb6fFt .rough-node .label text,#mermaid-svg-yc25xMS2DcVb6fFt .node .label text,#mermaid-svg-yc25xMS2DcVb6fFt .image-shape .label,#mermaid-svg-yc25xMS2DcVb6fFt .icon-shape .label{text-anchor:middle;}#mermaid-svg-yc25xMS2DcVb6fFt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yc25xMS2DcVb6fFt .rough-node .label,#mermaid-svg-yc25xMS2DcVb6fFt .node .label,#mermaid-svg-yc25xMS2DcVb6fFt .image-shape .label,#mermaid-svg-yc25xMS2DcVb6fFt .icon-shape .label{text-align:center;}#mermaid-svg-yc25xMS2DcVb6fFt .node.clickable{cursor:pointer;}#mermaid-svg-yc25xMS2DcVb6fFt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yc25xMS2DcVb6fFt .arrowheadPath{fill:#333333;}#mermaid-svg-yc25xMS2DcVb6fFt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yc25xMS2DcVb6fFt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yc25xMS2DcVb6fFt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yc25xMS2DcVb6fFt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yc25xMS2DcVb6fFt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yc25xMS2DcVb6fFt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yc25xMS2DcVb6fFt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yc25xMS2DcVb6fFt .cluster text{fill:#333;}#mermaid-svg-yc25xMS2DcVb6fFt .cluster span{color:#333;}#mermaid-svg-yc25xMS2DcVb6fFt 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-yc25xMS2DcVb6fFt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yc25xMS2DcVb6fFt rect.text{fill:none;stroke-width:0;}#mermaid-svg-yc25xMS2DcVb6fFt .icon-shape,#mermaid-svg-yc25xMS2DcVb6fFt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yc25xMS2DcVb6fFt .icon-shape p,#mermaid-svg-yc25xMS2DcVb6fFt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yc25xMS2DcVb6fFt .icon-shape .label rect,#mermaid-svg-yc25xMS2DcVb6fFt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yc25xMS2DcVb6fFt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yc25xMS2DcVb6fFt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yc25xMS2DcVb6fFt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} API Server
存储层
Reflector 层 (本模块)
Informer 层
用户层
用户 Controller
(Reconcile 逻辑)
SharedInformer
(生命周期管理)
sharedProcessor
(事件扇出)
Reflector
(数据入口)
ListerWatcher
(REST Client 封装)
DeltaFIFO / RealFIFO
(变更缓冲队列)
cache / Indexer
(本地缓存)
kube-apiserver
(etcd 数据源)
Reflector 是唯一与 API Server 直接交互的 Informer 组件。它将 REST 层的 List/Watch 语义转化为 Store 层的 Add/Update/Delete/Replace 语义。
1.3 数据流入流出
#mermaid-svg-G4UTcJaD34TEI5iE{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-G4UTcJaD34TEI5iE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-G4UTcJaD34TEI5iE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-G4UTcJaD34TEI5iE .error-icon{fill:#552222;}#mermaid-svg-G4UTcJaD34TEI5iE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-G4UTcJaD34TEI5iE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-G4UTcJaD34TEI5iE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-G4UTcJaD34TEI5iE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-G4UTcJaD34TEI5iE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-G4UTcJaD34TEI5iE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-G4UTcJaD34TEI5iE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-G4UTcJaD34TEI5iE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-G4UTcJaD34TEI5iE .marker.cross{stroke:#333333;}#mermaid-svg-G4UTcJaD34TEI5iE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-G4UTcJaD34TEI5iE p{margin:0;}#mermaid-svg-G4UTcJaD34TEI5iE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-G4UTcJaD34TEI5iE .cluster-label text{fill:#333;}#mermaid-svg-G4UTcJaD34TEI5iE .cluster-label span{color:#333;}#mermaid-svg-G4UTcJaD34TEI5iE .cluster-label span p{background-color:transparent;}#mermaid-svg-G4UTcJaD34TEI5iE .label text,#mermaid-svg-G4UTcJaD34TEI5iE span{fill:#333;color:#333;}#mermaid-svg-G4UTcJaD34TEI5iE .node rect,#mermaid-svg-G4UTcJaD34TEI5iE .node circle,#mermaid-svg-G4UTcJaD34TEI5iE .node ellipse,#mermaid-svg-G4UTcJaD34TEI5iE .node polygon,#mermaid-svg-G4UTcJaD34TEI5iE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-G4UTcJaD34TEI5iE .rough-node .label text,#mermaid-svg-G4UTcJaD34TEI5iE .node .label text,#mermaid-svg-G4UTcJaD34TEI5iE .image-shape .label,#mermaid-svg-G4UTcJaD34TEI5iE .icon-shape .label{text-anchor:middle;}#mermaid-svg-G4UTcJaD34TEI5iE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-G4UTcJaD34TEI5iE .rough-node .label,#mermaid-svg-G4UTcJaD34TEI5iE .node .label,#mermaid-svg-G4UTcJaD34TEI5iE .image-shape .label,#mermaid-svg-G4UTcJaD34TEI5iE .icon-shape .label{text-align:center;}#mermaid-svg-G4UTcJaD34TEI5iE .node.clickable{cursor:pointer;}#mermaid-svg-G4UTcJaD34TEI5iE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-G4UTcJaD34TEI5iE .arrowheadPath{fill:#333333;}#mermaid-svg-G4UTcJaD34TEI5iE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-G4UTcJaD34TEI5iE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-G4UTcJaD34TEI5iE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-G4UTcJaD34TEI5iE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-G4UTcJaD34TEI5iE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-G4UTcJaD34TEI5iE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-G4UTcJaD34TEI5iE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-G4UTcJaD34TEI5iE .cluster text{fill:#333;}#mermaid-svg-G4UTcJaD34TEI5iE .cluster span{color:#333;}#mermaid-svg-G4UTcJaD34TEI5iE 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-G4UTcJaD34TEI5iE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-G4UTcJaD34TEI5iE rect.text{fill:none;stroke-width:0;}#mermaid-svg-G4UTcJaD34TEI5iE .icon-shape,#mermaid-svg-G4UTcJaD34TEI5iE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-G4UTcJaD34TEI5iE .icon-shape p,#mermaid-svg-G4UTcJaD34TEI5iE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-G4UTcJaD34TEI5iE .icon-shape .label rect,#mermaid-svg-G4UTcJaD34TEI5iE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-G4UTcJaD34TEI5iE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-G4UTcJaD34TEI5iE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-G4UTcJaD34TEI5iE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据输出 (流出)
Reflector 处理
数据源 (流入)
List Response
(初始快照)
Watch Events
(增量变更)
WatchList Stream
(流式初始+增量)
syncWith()
(Replace Store)
handleAnyWatch()
(Add/Update/Delete)
startResync()
(Store.Resync)
store.Add()
store.Update()
store.Delete()
store.Replace()
store.Resync()
setLastSyncResourceVersion()
BookmarkStore.Bookmark()
二、模块整体结构
2.1 类结构图
#mermaid-svg-koArHMJzKSsVemBm{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-koArHMJzKSsVemBm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-koArHMJzKSsVemBm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-koArHMJzKSsVemBm .error-icon{fill:#552222;}#mermaid-svg-koArHMJzKSsVemBm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-koArHMJzKSsVemBm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-koArHMJzKSsVemBm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-koArHMJzKSsVemBm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-koArHMJzKSsVemBm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-koArHMJzKSsVemBm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-koArHMJzKSsVemBm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-koArHMJzKSsVemBm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-koArHMJzKSsVemBm .marker.cross{stroke:#333333;}#mermaid-svg-koArHMJzKSsVemBm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-koArHMJzKSsVemBm p{margin:0;}#mermaid-svg-koArHMJzKSsVemBm g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-koArHMJzKSsVemBm g.classGroup text .title{font-weight:bolder;}#mermaid-svg-koArHMJzKSsVemBm .cluster-label text{fill:#333;}#mermaid-svg-koArHMJzKSsVemBm .cluster-label span{color:#333;}#mermaid-svg-koArHMJzKSsVemBm .cluster-label span p{background-color:transparent;}#mermaid-svg-koArHMJzKSsVemBm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-koArHMJzKSsVemBm .cluster text{fill:#333;}#mermaid-svg-koArHMJzKSsVemBm .cluster span{color:#333;}#mermaid-svg-koArHMJzKSsVemBm .nodeLabel,#mermaid-svg-koArHMJzKSsVemBm .edgeLabel{color:#131300;}#mermaid-svg-koArHMJzKSsVemBm .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-koArHMJzKSsVemBm .label text{fill:#131300;}#mermaid-svg-koArHMJzKSsVemBm .labelBkg{background:#ECECFF;}#mermaid-svg-koArHMJzKSsVemBm .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-koArHMJzKSsVemBm .classTitle{font-weight:bolder;}#mermaid-svg-koArHMJzKSsVemBm .node rect,#mermaid-svg-koArHMJzKSsVemBm .node circle,#mermaid-svg-koArHMJzKSsVemBm .node ellipse,#mermaid-svg-koArHMJzKSsVemBm .node polygon,#mermaid-svg-koArHMJzKSsVemBm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-koArHMJzKSsVemBm .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-koArHMJzKSsVemBm g.clickable{cursor:pointer;}#mermaid-svg-koArHMJzKSsVemBm g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-koArHMJzKSsVemBm g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-koArHMJzKSsVemBm .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-koArHMJzKSsVemBm .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-koArHMJzKSsVemBm .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-koArHMJzKSsVemBm .dashed-line{stroke-dasharray:3;}#mermaid-svg-koArHMJzKSsVemBm .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-koArHMJzKSsVemBm #compositionStart,#mermaid-svg-koArHMJzKSsVemBm .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-koArHMJzKSsVemBm #compositionEnd,#mermaid-svg-koArHMJzKSsVemBm .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-koArHMJzKSsVemBm #dependencyStart,#mermaid-svg-koArHMJzKSsVemBm .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-koArHMJzKSsVemBm #dependencyStart,#mermaid-svg-koArHMJzKSsVemBm .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-koArHMJzKSsVemBm #extensionStart,#mermaid-svg-koArHMJzKSsVemBm .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-koArHMJzKSsVemBm #extensionEnd,#mermaid-svg-koArHMJzKSsVemBm .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-koArHMJzKSsVemBm #aggregationStart,#mermaid-svg-koArHMJzKSsVemBm .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-koArHMJzKSsVemBm #aggregationEnd,#mermaid-svg-koArHMJzKSsVemBm .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-koArHMJzKSsVemBm #lollipopStart,#mermaid-svg-koArHMJzKSsVemBm .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-koArHMJzKSsVemBm #lollipopEnd,#mermaid-svg-koArHMJzKSsVemBm .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-koArHMJzKSsVemBm .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-koArHMJzKSsVemBm .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-koArHMJzKSsVemBm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-koArHMJzKSsVemBm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-koArHMJzKSsVemBm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} store
listerWatcher
watchErrorHandler
created by
uses
extends
optional
optional
Reflector
-logger klog.Logger
-name string
-typeDescription string
-expectedType reflect.Type
-expectedGVK *schema.GroupVersionKind
-store ReflectorStore
-listerWatcher ListerWatcherWithContext
-resyncPeriod time.Duration
-delayHandler wait.DelayFunc
-minWatchTimeout time.Duration
-maxWatchTimeout time.Duration
-clock clock.Clock
-paginatedResult bool
-lastSyncResourceVersion string
-isLastSyncResourceVersionUnavailable bool
-lastSyncResourceVersionMutex sync.RWMutex
-watchErrorHandler WatchErrorHandlerWithContext
-WatchListPageSize int64
-MaxInternalErrorRetryDuration time.Duration
-useWatchList bool
-ShouldResync func() : bool
+Name() : string
+TypeDescription() : string
+Run(stopCh)
+RunWithContext(ctx)
+ListAndWatch(stopCh) : error
+ListAndWatchWithContext(ctx) : error
+LastSyncResourceVersion() : string
-list(ctx) : error
-watchList(ctx)(watch.Interface, error)
-watchWithResync(ctx, w) : error
-watch(ctx, w, resyncerrc) : error
-startResync(ctx, resyncerrc)
-syncWith(items, rv) : error
-resyncChan() (chan time.Time, func() bool)
-relistResourceVersion() : string
-rewatchResourceVersion() : string
-setLastSyncResourceVersion(v)
-setIsLastSyncResourceVersionUnavailable(bool)
<<interface>>
ReflectorStore
+Add(obj) : error
+Update(obj) : error
+Delete(obj) : error
+Replace(items, rv) : error
+Resync() : error
<<interface>>
ReflectorBookmarkStore
+Bookmark(rv) : error
<<interface>>
TransformingStore
+ReflectorStore methods
+Transformer() : TransformFunc
<<interface>>
ResourceVersionUpdater
+UpdateResourceVersion(rv)
<<interface>>
ListerWatcherWithContext
+ListWithContext(ctx, opts)(runtime.Object, error)
+WatchWithContext(ctx, opts)(watch.Interface, error)
<<interface-function>>
WatchErrorHandlerWithContext
+Call(ctx, r, err)
ReflectorOptions
+Logger *klog.Logger
+Name string
+TypeDescription string
+ResyncPeriod time.Duration
+MinWatchTimeout time.Duration
+Clock clock.Clock
+Backoff *wait.Backoff
initialEventsEndBookmarkTicker
-clock.Ticker
-clock clock.Clock
-name string
-logger klog.Logger
-watchStart time.Time
-tickInterval time.Duration
-lastEventObserveTime time.Time
+observeLastEventTimeStamp(time)
+warnIfExpired()
+produceWarningIfExpired() : error
VeryShortWatchError
+Name string
+Error() : string
2.2 接口定义详解
ReflectorStore --- Reflector 对 Store 的最小需求
Reflector 不直接使用完整的 Store 接口(有 Get/List/ListKeys 等查询方法),而只依赖写入方法:
go
type ReflectorStore interface {
Add(obj interface{}) error // 添加单个对象
Update(obj interface{}) error // 更新单个对象
Delete(obj interface{}) error // 删除单个对象
Replace([]interface{}, string) error // 替换整个 Store 内容(初始 List / WatchList 后调用)
Resync() error // 重新同步(触发 DeltaFIFO 的 Resync 语义)
}
设计意图 :Reflector 是数据的纯生产者,只需要写入能力,不需要查询。查询是 Lister/Indexer 的职责。
ReflectorBookmarkStore --- Bookmark 事件通知
go
type ReflectorBookmarkStore interface {
Bookmark(resourceVersion string) error
}
设计意图:Bookmark 事件只携带 RV 不携带对象,用于通知 Store 当前进度。DeltaFIFO 实现了此接口,在收到 Bookmark 时更新 RV 以支持 AtomicFIFO 的 RV 追踪。
TransformingStore --- 变换函数传递
go
type TransformingStore interface {
ReflectorStore
Transformer() TransformFunc // 返回 Store 的 TransformFunc
}
设计意图:当 Store(如 DeltaFIFO)配置了 TransformFunc 时,Reflector 的 watchList 临时 Store 也应使用相同的 TransformFunc,确保数据变换一致。
ResourceVersionUpdater --- RV 进度同步
go
type ResourceVersionUpdater interface {
UpdateResourceVersion(resourceVersion string)
}
设计意图:在每次 RV 更新时通知 Store。这对支持 Bookmark 的 Store(如 RealFIFO)特别重要------RV 更新不需要产生 Delta,但 Store 需要知道当前进度。
WatchErrorHandlerWithContext --- Watch 错误回调
go
type WatchErrorHandlerWithContext func(ctx context.Context, r *Reflector, err error)
默认实现 DefaultWatchErrorHandler:
| 错误类型 | 日志级别 | 处理 |
|---|---|---|
isExpiredError (410 Gone) |
V(4) | 不设置 RV Unavailable,直接用旧 RV 重试 |
io.EOF |
--- | Watch 正常关闭,静默 |
io.ErrUnexpectedEOF |
V(1) | 不正常关闭,警告 |
| 其他 | HandleError | 通用错误处理 |
2.3 Reflector 字段详解
核心字段
| 字段 | 类型 | 作用 | 设计意图 |
|---|---|---|---|
name |
string | Reflector 标识,用于日志和指标 | 默认取调用栈文件名:行号,避免空名 |
typeDescription |
string | 资源类型描述(如 *v1.Pod) |
纯展示用,不参与逻辑判断 |
expectedType |
reflect.Type | 期望的对象 Go 类型 | Watch 事件类型过滤 |
expectedGVK |
*schema.GroupVersionKind | 期望的 GVK(仅 Unstructured 使用) | Unstructured 类型无法通过 reflect.Type 区分 GVK |
store |
ReflectorStore | 数据写入目标 | 通常是 DeltaFIFO 或 RealFIFO |
listerWatcher |
ListerWatcherWithContext | REST Client 封装 | 通过构造注入,支持 mock |
resyncPeriod |
time.Duration | Resync 周期 | 0 = 不 Resync |
delayHandler |
wait.DelayFunc | 退避延迟函数 | 由 Backoff.DelayWithReset 生成 |
超时与重试字段
| 字段 | 默认值 | 作用 |
|---|---|---|
minWatchTimeout |
5min | Watch 请求最小超时 |
maxWatchTimeout |
10min | Watch 请求最大超时(实际随机在 min, max) |
MaxInternalErrorRetryDuration |
--- | Internal Error 重试最大时长 |
WatchListPageSize |
--- | 强制分页大小(0=自动决策) |
状态字段
| 字段 | 类型 | 作用 | 线程安全 |
|---|---|---|---|
lastSyncResourceVersion |
string | 最后观察到的 RV | ✅ RWMutex 保护 |
isLastSyncResourceVersionUnavailable |
bool | RV 是否过期/过大 | ✅ RWMutex 保护 |
paginatedResult |
bool | 首次 List 是否分页 | 仅单 goroutine 写 |
useWatchList |
bool | 是否启用 WatchList 模式 | 构造时确定,不变 |
退避参数常量
go
var (
defaultMinWatchTimeout = 5 * time.Minute // Watch 最小超时
defaultMaxWatchTimeout = 2 * defaultMinWatchTimeout // 10min
defaultBackoffInit = 800 * time.Millisecond // 退避初始间隔
defaultBackoffMax = 30 * time.Second // 退避最大间隔
defaultBackoffReset = 2 * time.Minute // 退避重置窗口
defaultBackoffFactor = 2.0 // 退避倍数
defaultBackoffJitter = 1.0 // 退避抖动(0~1.0s 随机)
)
退避 QPS 计算:最大退避 30s → 理论上限 1/30 ≈ 0.033 QPS,加上 [30, 60) 的抖动 → ~0.22 QPS。目标是在 API Server 不健康时将流量降低 98%。
2.4 依赖注入关系
#mermaid-svg-r7GuB2fZjVsSJvAn{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-r7GuB2fZjVsSJvAn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-r7GuB2fZjVsSJvAn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-r7GuB2fZjVsSJvAn .error-icon{fill:#552222;}#mermaid-svg-r7GuB2fZjVsSJvAn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-r7GuB2fZjVsSJvAn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-r7GuB2fZjVsSJvAn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-r7GuB2fZjVsSJvAn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-r7GuB2fZjVsSJvAn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-r7GuB2fZjVsSJvAn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-r7GuB2fZjVsSJvAn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-r7GuB2fZjVsSJvAn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-r7GuB2fZjVsSJvAn .marker.cross{stroke:#333333;}#mermaid-svg-r7GuB2fZjVsSJvAn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-r7GuB2fZjVsSJvAn p{margin:0;}#mermaid-svg-r7GuB2fZjVsSJvAn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-r7GuB2fZjVsSJvAn .cluster-label text{fill:#333;}#mermaid-svg-r7GuB2fZjVsSJvAn .cluster-label span{color:#333;}#mermaid-svg-r7GuB2fZjVsSJvAn .cluster-label span p{background-color:transparent;}#mermaid-svg-r7GuB2fZjVsSJvAn .label text,#mermaid-svg-r7GuB2fZjVsSJvAn span{fill:#333;color:#333;}#mermaid-svg-r7GuB2fZjVsSJvAn .node rect,#mermaid-svg-r7GuB2fZjVsSJvAn .node circle,#mermaid-svg-r7GuB2fZjVsSJvAn .node ellipse,#mermaid-svg-r7GuB2fZjVsSJvAn .node polygon,#mermaid-svg-r7GuB2fZjVsSJvAn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-r7GuB2fZjVsSJvAn .rough-node .label text,#mermaid-svg-r7GuB2fZjVsSJvAn .node .label text,#mermaid-svg-r7GuB2fZjVsSJvAn .image-shape .label,#mermaid-svg-r7GuB2fZjVsSJvAn .icon-shape .label{text-anchor:middle;}#mermaid-svg-r7GuB2fZjVsSJvAn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-r7GuB2fZjVsSJvAn .rough-node .label,#mermaid-svg-r7GuB2fZjVsSJvAn .node .label,#mermaid-svg-r7GuB2fZjVsSJvAn .image-shape .label,#mermaid-svg-r7GuB2fZjVsSJvAn .icon-shape .label{text-align:center;}#mermaid-svg-r7GuB2fZjVsSJvAn .node.clickable{cursor:pointer;}#mermaid-svg-r7GuB2fZjVsSJvAn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-r7GuB2fZjVsSJvAn .arrowheadPath{fill:#333333;}#mermaid-svg-r7GuB2fZjVsSJvAn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-r7GuB2fZjVsSJvAn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-r7GuB2fZjVsSJvAn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r7GuB2fZjVsSJvAn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-r7GuB2fZjVsSJvAn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r7GuB2fZjVsSJvAn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-r7GuB2fZjVsSJvAn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-r7GuB2fZjVsSJvAn .cluster text{fill:#333;}#mermaid-svg-r7GuB2fZjVsSJvAn .cluster span{color:#333;}#mermaid-svg-r7GuB2fZjVsSJvAn 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-r7GuB2fZjVsSJvAn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-r7GuB2fZjVsSJvAn rect.text{fill:none;stroke-width:0;}#mermaid-svg-r7GuB2fZjVsSJvAn .icon-shape,#mermaid-svg-r7GuB2fZjVsSJvAn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-r7GuB2fZjVsSJvAn .icon-shape p,#mermaid-svg-r7GuB2fZjVsSJvAn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-r7GuB2fZjVsSJvAn .icon-shape .label rect,#mermaid-svg-r7GuB2fZjVsSJvAn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-r7GuB2fZjVsSJvAn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-r7GuB2fZjVsSJvAn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-r7GuB2fZjVsSJvAn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Reflector 内部
注入的依赖
ReflectorOptions
构造入口
NewReflector
(legacy)
NewNamedReflector
(legacy)
NewReflectorWithOptions
(推荐)
NewNamespaceKeyedIndexerAndReflector
Name
Logger
TypeDescription
ResyncPeriod
MinWatchTimeout
Clock
Backoff
ListerWatcher
ReflectorStore
expectedType
Reflector 实例
delayHandler
(由 Backoff 生成)
useWatchList
(Feature Gate 决定)
NewReflectorWithOptions 构造流程
#mermaid-svg-E7v0fZBIeM8HDhFo{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-E7v0fZBIeM8HDhFo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-E7v0fZBIeM8HDhFo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-E7v0fZBIeM8HDhFo .error-icon{fill:#552222;}#mermaid-svg-E7v0fZBIeM8HDhFo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-E7v0fZBIeM8HDhFo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-E7v0fZBIeM8HDhFo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-E7v0fZBIeM8HDhFo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-E7v0fZBIeM8HDhFo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-E7v0fZBIeM8HDhFo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-E7v0fZBIeM8HDhFo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-E7v0fZBIeM8HDhFo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-E7v0fZBIeM8HDhFo .marker.cross{stroke:#333333;}#mermaid-svg-E7v0fZBIeM8HDhFo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-E7v0fZBIeM8HDhFo p{margin:0;}#mermaid-svg-E7v0fZBIeM8HDhFo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-E7v0fZBIeM8HDhFo .cluster-label text{fill:#333;}#mermaid-svg-E7v0fZBIeM8HDhFo .cluster-label span{color:#333;}#mermaid-svg-E7v0fZBIeM8HDhFo .cluster-label span p{background-color:transparent;}#mermaid-svg-E7v0fZBIeM8HDhFo .label text,#mermaid-svg-E7v0fZBIeM8HDhFo span{fill:#333;color:#333;}#mermaid-svg-E7v0fZBIeM8HDhFo .node rect,#mermaid-svg-E7v0fZBIeM8HDhFo .node circle,#mermaid-svg-E7v0fZBIeM8HDhFo .node ellipse,#mermaid-svg-E7v0fZBIeM8HDhFo .node polygon,#mermaid-svg-E7v0fZBIeM8HDhFo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-E7v0fZBIeM8HDhFo .rough-node .label text,#mermaid-svg-E7v0fZBIeM8HDhFo .node .label text,#mermaid-svg-E7v0fZBIeM8HDhFo .image-shape .label,#mermaid-svg-E7v0fZBIeM8HDhFo .icon-shape .label{text-anchor:middle;}#mermaid-svg-E7v0fZBIeM8HDhFo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-E7v0fZBIeM8HDhFo .rough-node .label,#mermaid-svg-E7v0fZBIeM8HDhFo .node .label,#mermaid-svg-E7v0fZBIeM8HDhFo .image-shape .label,#mermaid-svg-E7v0fZBIeM8HDhFo .icon-shape .label{text-align:center;}#mermaid-svg-E7v0fZBIeM8HDhFo .node.clickable{cursor:pointer;}#mermaid-svg-E7v0fZBIeM8HDhFo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-E7v0fZBIeM8HDhFo .arrowheadPath{fill:#333333;}#mermaid-svg-E7v0fZBIeM8HDhFo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-E7v0fZBIeM8HDhFo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-E7v0fZBIeM8HDhFo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-E7v0fZBIeM8HDhFo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-E7v0fZBIeM8HDhFo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-E7v0fZBIeM8HDhFo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-E7v0fZBIeM8HDhFo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-E7v0fZBIeM8HDhFo .cluster text{fill:#333;}#mermaid-svg-E7v0fZBIeM8HDhFo .cluster span{color:#333;}#mermaid-svg-E7v0fZBIeM8HDhFo 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-E7v0fZBIeM8HDhFo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-E7v0fZBIeM8HDhFo rect.text{fill:none;stroke-width:0;}#mermaid-svg-E7v0fZBIeM8HDhFo .icon-shape,#mermaid-svg-E7v0fZBIeM8HDhFo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-E7v0fZBIeM8HDhFo .icon-shape p,#mermaid-svg-E7v0fZBIeM8HDhFo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-E7v0fZBIeM8HDhFo .icon-shape .label rect,#mermaid-svg-E7v0fZBIeM8HDhFo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-E7v0fZBIeM8HDhFo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-E7v0fZBIeM8HDhFo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-E7v0fZBIeM8HDhFo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Yes
Yes
No
No
NewReflectorWithOptions(lw, expectedType, store, options)
确定 clock
options.Clock || clock.RealClock{}
确定 timeout
min=5min, max=2*min
options.MinWatchTimeout > 5min → 覆盖
maxWatchTimeout < minWatchTimeout?
强制 maxWatchTimeout = minWatchTimeout
确定 backoff
options.Backoff || 默认退避参数
创建 delayHandler
backoff.DelayWithReset(clock, 2min)
确定 name
options.Name || 调用栈文件名:行号
确定 typeDescription
options.TypeDescription || getTypeDescriptionFromObject()
确定 expectedGVK
getExpectedGVKFromObject()
listerWatcher = ToListerWatcherWithContext(lw)
(适配旧接口)
Feature Gate
WatchListClient 启用?
watchlist.DoesClientNotSupportWatchListSemantics(lw)?
useWatchList = true
useWatchList = false
(Client 不支持)
创建 Reflector 实例
logger = klog.LoggerWithName(logger, name)
2.5 核心方法清单
公开方法
| 方法 | 作用 | 调用方 |
|---|---|---|
Name() |
返回 Reflector 名称 | Controller / 日志 |
TypeDescription() |
返回资源类型描述 | 日志 |
Run(stopCh) |
启动主循环(legacy) | Controller |
RunWithContext(ctx) |
启动主循环(推荐) | Controller |
ListAndWatch(stopCh) |
单次 List + Watch 循环 | 极少直接调用 |
ListAndWatchWithContext(ctx) |
单次 List + Watch 循环 | RunWithContext 调用 |
LastSyncResourceVersion() |
获取最后同步 RV | SharedInformer |
私有方法
| 方法 | 作用 | 行数(约) |
|---|---|---|
list(ctx) |
执行 List 请求 + 分页 + syncWith | ~100 |
watchList(ctx) |
执行 WatchList 流式请求 | ~80 |
watchWithResync(ctx, w) |
Watch + 后台 Resync | ~15 |
watch(ctx, w, resyncerrc) |
Watch 主循环 | ~60 |
startResync(ctx, resyncerrc) |
后台 Resync 协程 | ~20 |
syncWith(items, rv) |
将 List 结果 Replace 到 Store | ~5 |
resyncChan() |
返回 Resync 定时器 channel | ~10 |
relistResourceVersion() |
决定 List 使用的 RV | ~15 |
rewatchResourceVersion() |
决定 WatchList 使用的 RV | ~10 |
setLastSyncResourceVersion(v) |
设置 RV(加写锁) | ~4 |
setIsLastSyncResourceVersionUnavailable(b) |
设置 RV 不可用标记 | ~4 |
2.6 内部调用关系 --- 完整调用链
#mermaid-svg-kKLbfwh4iCbbjNBm{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-kKLbfwh4iCbbjNBm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kKLbfwh4iCbbjNBm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kKLbfwh4iCbbjNBm .error-icon{fill:#552222;}#mermaid-svg-kKLbfwh4iCbbjNBm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kKLbfwh4iCbbjNBm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kKLbfwh4iCbbjNBm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kKLbfwh4iCbbjNBm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kKLbfwh4iCbbjNBm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kKLbfwh4iCbbjNBm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kKLbfwh4iCbbjNBm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kKLbfwh4iCbbjNBm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kKLbfwh4iCbbjNBm .marker.cross{stroke:#333333;}#mermaid-svg-kKLbfwh4iCbbjNBm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kKLbfwh4iCbbjNBm p{margin:0;}#mermaid-svg-kKLbfwh4iCbbjNBm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kKLbfwh4iCbbjNBm .cluster-label text{fill:#333;}#mermaid-svg-kKLbfwh4iCbbjNBm .cluster-label span{color:#333;}#mermaid-svg-kKLbfwh4iCbbjNBm .cluster-label span p{background-color:transparent;}#mermaid-svg-kKLbfwh4iCbbjNBm .label text,#mermaid-svg-kKLbfwh4iCbbjNBm span{fill:#333;color:#333;}#mermaid-svg-kKLbfwh4iCbbjNBm .node rect,#mermaid-svg-kKLbfwh4iCbbjNBm .node circle,#mermaid-svg-kKLbfwh4iCbbjNBm .node ellipse,#mermaid-svg-kKLbfwh4iCbbjNBm .node polygon,#mermaid-svg-kKLbfwh4iCbbjNBm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kKLbfwh4iCbbjNBm .rough-node .label text,#mermaid-svg-kKLbfwh4iCbbjNBm .node .label text,#mermaid-svg-kKLbfwh4iCbbjNBm .image-shape .label,#mermaid-svg-kKLbfwh4iCbbjNBm .icon-shape .label{text-anchor:middle;}#mermaid-svg-kKLbfwh4iCbbjNBm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kKLbfwh4iCbbjNBm .rough-node .label,#mermaid-svg-kKLbfwh4iCbbjNBm .node .label,#mermaid-svg-kKLbfwh4iCbbjNBm .image-shape .label,#mermaid-svg-kKLbfwh4iCbbjNBm .icon-shape .label{text-align:center;}#mermaid-svg-kKLbfwh4iCbbjNBm .node.clickable{cursor:pointer;}#mermaid-svg-kKLbfwh4iCbbjNBm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kKLbfwh4iCbbjNBm .arrowheadPath{fill:#333333;}#mermaid-svg-kKLbfwh4iCbbjNBm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kKLbfwh4iCbbjNBm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kKLbfwh4iCbbjNBm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kKLbfwh4iCbbjNBm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kKLbfwh4iCbbjNBm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kKLbfwh4iCbbjNBm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kKLbfwh4iCbbjNBm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kKLbfwh4iCbbjNBm .cluster text{fill:#333;}#mermaid-svg-kKLbfwh4iCbbjNBm .cluster span{color:#333;}#mermaid-svg-kKLbfwh4iCbbjNBm 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-kKLbfwh4iCbbjNBm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kKLbfwh4iCbbjNBm rect.text{fill:none;stroke-width:0;}#mermaid-svg-kKLbfwh4iCbbjNBm .icon-shape,#mermaid-svg-kKLbfwh4iCbbjNBm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kKLbfwh4iCbbjNBm .icon-shape p,#mermaid-svg-kKLbfwh4iCbbjNBm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kKLbfwh4iCbbjNBm .icon-shape .label rect,#mermaid-svg-kKLbfwh4iCbbjNBm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kKLbfwh4iCbbjNBm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kKLbfwh4iCbbjNBm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kKLbfwh4iCbbjNBm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} true
成功
失败
false
error
RunWithContext(ctx)
delayHandler.Until(ctx,
immediate=true, sliding=true)
ListAndWatchWithContext(ctx)
useWatchList?
watchList(ctx)
watchList 成功?
fallbackToList = true
list(ctx)
watchWithResync(ctx, w)
startResync(ctx, resyncerrc)
watch(ctx, w, resyncerrc)
Watch 循环
listerWatcher.WatchWithContext()
handleAnyWatch()
Resync 定时器?
ShouldResync()?
store.Resync()
watchErrorHandler(ctx, r, err)
2.7 ReflectorOptions 字段详解
go
type ReflectorOptions struct {
Logger *klog.Logger // 可选日志器,默认 klog.Background()
Name string // 可选名称,默认调用栈文件名:行号
TypeDescription string // 可选类型描述,默认从 expectedType 推导
ResyncPeriod time.Duration // Resync 周期,0=不 Resync
MinWatchTimeout time.Duration // Watch 最小超时,<5min 不生效
Clock clock.Clock // 可注入时钟(测试用 FakeClock)
Backoff *wait.Backoff // 可选退避配置
}
Backoff 默认值:
go
&wait.Backoff{
Duration: 800ms, // 初始间隔
Cap: 30s, // 最大间隔
Steps: 38, // ceil(30/0.8) = 38 步到顶
Factor: 2.0, // 每步翻倍
Jitter: 1.0, // [0, 1.0)s 随机抖动
}
退避序列(无抖动时):800ms → 1.6s → 3.2s → 6.4s → 12.8s → 25.6s → 30s → 30s → ...
DelayWithReset:超过 2min 未出错则重置到 800ms。
2.8 getTypeDescriptionFromObject --- 类型描述推导
go
func getTypeDescriptionFromObject(expectedType interface{}) string {
if expectedType == nil {
return defaultExpectedTypeName // "<unspecified>"
}
// 默认使用 reflect.Type.String(),如 "*v1.Pod"
reflectDescription := reflect.TypeOf(expectedType).String()
// 特殊处理:Unstructured 需要从 apiVersion+kind 推导
obj, ok := expectedType.(*unstructured.Unstructured)
if !ok {
return reflectDescription
}
gvk := obj.GroupVersionKind()
if gvk.Empty() {
return reflectDescription // GVK 未设 → 退化为 reflect.Type
}
return gvk.String() // "apps/v1, Kind=Deployment"
}
为什么 Unstructured 需要 GVK? 因为 *unstructured.Unstructured 对所有资源类型都是同一个 Go 类型,无法通过 reflect.Type 区分。GVK 提供了精确的类型标识。
2.9 name 默认值 --- 调用栈推导
go
var internalPackages = []string{"client-go/tools/cache/"}
// 如果 options.Name 为空,从调用栈获取
r.name = naming.GetNameFromCallsite(internalPackages...)
// 例如:pkg/controller/deployment/deployment_controller.go:42
设计意图 :自动命名避免了日志中所有 Reflector 都叫 "unnamed" 的问题。通过跳过 client-go/tools/cache/ 内部的调用帧,定位到用户代码的调用位置。
三、关键常量与包级变量
3.1 错误类型
| 变量 | 值 | 作用 |
|---|---|---|
errorStopRequested |
errors.New("stop requested") |
ctx 取消时的正常退出标记,不是真正的 error |
3.2 Watch 超时随机化
#mermaid-svg-B1nRU1RRCbTihgMl{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-B1nRU1RRCbTihgMl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-B1nRU1RRCbTihgMl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-B1nRU1RRCbTihgMl .error-icon{fill:#552222;}#mermaid-svg-B1nRU1RRCbTihgMl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-B1nRU1RRCbTihgMl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-B1nRU1RRCbTihgMl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-B1nRU1RRCbTihgMl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-B1nRU1RRCbTihgMl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-B1nRU1RRCbTihgMl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-B1nRU1RRCbTihgMl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-B1nRU1RRCbTihgMl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-B1nRU1RRCbTihgMl .marker.cross{stroke:#333333;}#mermaid-svg-B1nRU1RRCbTihgMl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-B1nRU1RRCbTihgMl p{margin:0;}#mermaid-svg-B1nRU1RRCbTihgMl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-B1nRU1RRCbTihgMl .cluster-label text{fill:#333;}#mermaid-svg-B1nRU1RRCbTihgMl .cluster-label span{color:#333;}#mermaid-svg-B1nRU1RRCbTihgMl .cluster-label span p{background-color:transparent;}#mermaid-svg-B1nRU1RRCbTihgMl .label text,#mermaid-svg-B1nRU1RRCbTihgMl span{fill:#333;color:#333;}#mermaid-svg-B1nRU1RRCbTihgMl .node rect,#mermaid-svg-B1nRU1RRCbTihgMl .node circle,#mermaid-svg-B1nRU1RRCbTihgMl .node ellipse,#mermaid-svg-B1nRU1RRCbTihgMl .node polygon,#mermaid-svg-B1nRU1RRCbTihgMl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-B1nRU1RRCbTihgMl .rough-node .label text,#mermaid-svg-B1nRU1RRCbTihgMl .node .label text,#mermaid-svg-B1nRU1RRCbTihgMl .image-shape .label,#mermaid-svg-B1nRU1RRCbTihgMl .icon-shape .label{text-anchor:middle;}#mermaid-svg-B1nRU1RRCbTihgMl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-B1nRU1RRCbTihgMl .rough-node .label,#mermaid-svg-B1nRU1RRCbTihgMl .node .label,#mermaid-svg-B1nRU1RRCbTihgMl .image-shape .label,#mermaid-svg-B1nRU1RRCbTihgMl .icon-shape .label{text-align:center;}#mermaid-svg-B1nRU1RRCbTihgMl .node.clickable{cursor:pointer;}#mermaid-svg-B1nRU1RRCbTihgMl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-B1nRU1RRCbTihgMl .arrowheadPath{fill:#333333;}#mermaid-svg-B1nRU1RRCbTihgMl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-B1nRU1RRCbTihgMl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-B1nRU1RRCbTihgMl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B1nRU1RRCbTihgMl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-B1nRU1RRCbTihgMl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B1nRU1RRCbTihgMl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-B1nRU1RRCbTihgMl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-B1nRU1RRCbTihgMl .cluster text{fill:#333;}#mermaid-svg-B1nRU1RRCbTihgMl .cluster span{color:#333;}#mermaid-svg-B1nRU1RRCbTihgMl 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-B1nRU1RRCbTihgMl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-B1nRU1RRCbTihgMl rect.text{fill:none;stroke-width:0;}#mermaid-svg-B1nRU1RRCbTihgMl .icon-shape,#mermaid-svg-B1nRU1RRCbTihgMl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B1nRU1RRCbTihgMl .icon-shape p,#mermaid-svg-B1nRU1RRCbTihgMl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-B1nRU1RRCbTihgMl .icon-shape .label rect,#mermaid-svg-B1nRU1RRCbTihgMl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B1nRU1RRCbTihgMl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-B1nRU1RRCbTihgMl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-B1nRU1RRCbTihgMl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} minWatchTimeout
5min
maxWatchTimeout
10min
rand.Float64()
max - min = 5min
TimeoutSeconds =
min + rand * (max - min)
∈ [5min, 10min)
设计意图:随机化超时避免所有 Reflector 同时超时重连(惊群效应),分散 API Server 负载。
3.3 退避重置窗口
#mermaid-svg-oE6FNkFuRpI6hinH{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-oE6FNkFuRpI6hinH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oE6FNkFuRpI6hinH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oE6FNkFuRpI6hinH .error-icon{fill:#552222;}#mermaid-svg-oE6FNkFuRpI6hinH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oE6FNkFuRpI6hinH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oE6FNkFuRpI6hinH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oE6FNkFuRpI6hinH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oE6FNkFuRpI6hinH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oE6FNkFuRpI6hinH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oE6FNkFuRpI6hinH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oE6FNkFuRpI6hinH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oE6FNkFuRpI6hinH .marker.cross{stroke:#333333;}#mermaid-svg-oE6FNkFuRpI6hinH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oE6FNkFuRpI6hinH p{margin:0;}#mermaid-svg-oE6FNkFuRpI6hinH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oE6FNkFuRpI6hinH .cluster-label text{fill:#333;}#mermaid-svg-oE6FNkFuRpI6hinH .cluster-label span{color:#333;}#mermaid-svg-oE6FNkFuRpI6hinH .cluster-label span p{background-color:transparent;}#mermaid-svg-oE6FNkFuRpI6hinH .label text,#mermaid-svg-oE6FNkFuRpI6hinH span{fill:#333;color:#333;}#mermaid-svg-oE6FNkFuRpI6hinH .node rect,#mermaid-svg-oE6FNkFuRpI6hinH .node circle,#mermaid-svg-oE6FNkFuRpI6hinH .node ellipse,#mermaid-svg-oE6FNkFuRpI6hinH .node polygon,#mermaid-svg-oE6FNkFuRpI6hinH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oE6FNkFuRpI6hinH .rough-node .label text,#mermaid-svg-oE6FNkFuRpI6hinH .node .label text,#mermaid-svg-oE6FNkFuRpI6hinH .image-shape .label,#mermaid-svg-oE6FNkFuRpI6hinH .icon-shape .label{text-anchor:middle;}#mermaid-svg-oE6FNkFuRpI6hinH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oE6FNkFuRpI6hinH .rough-node .label,#mermaid-svg-oE6FNkFuRpI6hinH .node .label,#mermaid-svg-oE6FNkFuRpI6hinH .image-shape .label,#mermaid-svg-oE6FNkFuRpI6hinH .icon-shape .label{text-align:center;}#mermaid-svg-oE6FNkFuRpI6hinH .node.clickable{cursor:pointer;}#mermaid-svg-oE6FNkFuRpI6hinH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oE6FNkFuRpI6hinH .arrowheadPath{fill:#333333;}#mermaid-svg-oE6FNkFuRpI6hinH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oE6FNkFuRpI6hinH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oE6FNkFuRpI6hinH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oE6FNkFuRpI6hinH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oE6FNkFuRpI6hinH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oE6FNkFuRpI6hinH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oE6FNkFuRpI6hinH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oE6FNkFuRpI6hinH .cluster text{fill:#333;}#mermaid-svg-oE6FNkFuRpI6hinH .cluster span{color:#333;}#mermaid-svg-oE6FNkFuRpI6hinH 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-oE6FNkFuRpI6hinH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oE6FNkFuRpI6hinH rect.text{fill:none;stroke-width:0;}#mermaid-svg-oE6FNkFuRpI6hinH .icon-shape,#mermaid-svg-oE6FNkFuRpI6hinH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oE6FNkFuRpI6hinH .icon-shape p,#mermaid-svg-oE6FNkFuRpI6hinH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oE6FNkFuRpI6hinH .icon-shape .label rect,#mermaid-svg-oE6FNkFuRpI6hinH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oE6FNkFuRpI6hinH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oE6FNkFuRpI6hinH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oE6FNkFuRpI6hinH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 持续退避
正常退避
Yes
No
Yes
800ms
1.6s
3.2s
6.4s
2min 内无新错误?
重置到 800ms
12.8s
25.6s
30s(cap)
30s
30s
2min 内无新错误?
四、辅助类型详解
4.1 initialEventsEndBookmarkTicker --- Bookmark 超时检测
#mermaid-svg-uhwWJGCSudt1j5CV{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-uhwWJGCSudt1j5CV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uhwWJGCSudt1j5CV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uhwWJGCSudt1j5CV .error-icon{fill:#552222;}#mermaid-svg-uhwWJGCSudt1j5CV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uhwWJGCSudt1j5CV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uhwWJGCSudt1j5CV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uhwWJGCSudt1j5CV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uhwWJGCSudt1j5CV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uhwWJGCSudt1j5CV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uhwWJGCSudt1j5CV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uhwWJGCSudt1j5CV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uhwWJGCSudt1j5CV .marker.cross{stroke:#333333;}#mermaid-svg-uhwWJGCSudt1j5CV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uhwWJGCSudt1j5CV p{margin:0;}#mermaid-svg-uhwWJGCSudt1j5CV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uhwWJGCSudt1j5CV .cluster-label text{fill:#333;}#mermaid-svg-uhwWJGCSudt1j5CV .cluster-label span{color:#333;}#mermaid-svg-uhwWJGCSudt1j5CV .cluster-label span p{background-color:transparent;}#mermaid-svg-uhwWJGCSudt1j5CV .label text,#mermaid-svg-uhwWJGCSudt1j5CV span{fill:#333;color:#333;}#mermaid-svg-uhwWJGCSudt1j5CV .node rect,#mermaid-svg-uhwWJGCSudt1j5CV .node circle,#mermaid-svg-uhwWJGCSudt1j5CV .node ellipse,#mermaid-svg-uhwWJGCSudt1j5CV .node polygon,#mermaid-svg-uhwWJGCSudt1j5CV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uhwWJGCSudt1j5CV .rough-node .label text,#mermaid-svg-uhwWJGCSudt1j5CV .node .label text,#mermaid-svg-uhwWJGCSudt1j5CV .image-shape .label,#mermaid-svg-uhwWJGCSudt1j5CV .icon-shape .label{text-anchor:middle;}#mermaid-svg-uhwWJGCSudt1j5CV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uhwWJGCSudt1j5CV .rough-node .label,#mermaid-svg-uhwWJGCSudt1j5CV .node .label,#mermaid-svg-uhwWJGCSudt1j5CV .image-shape .label,#mermaid-svg-uhwWJGCSudt1j5CV .icon-shape .label{text-align:center;}#mermaid-svg-uhwWJGCSudt1j5CV .node.clickable{cursor:pointer;}#mermaid-svg-uhwWJGCSudt1j5CV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uhwWJGCSudt1j5CV .arrowheadPath{fill:#333333;}#mermaid-svg-uhwWJGCSudt1j5CV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uhwWJGCSudt1j5CV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uhwWJGCSudt1j5CV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uhwWJGCSudt1j5CV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uhwWJGCSudt1j5CV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uhwWJGCSudt1j5CV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uhwWJGCSudt1j5CV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uhwWJGCSudt1j5CV .cluster text{fill:#333;}#mermaid-svg-uhwWJGCSudt1j5CV .cluster span{color:#333;}#mermaid-svg-uhwWJGCSudt1j5CV 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-uhwWJGCSudt1j5CV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uhwWJGCSudt1j5CV rect.text{fill:none;stroke-width:0;}#mermaid-svg-uhwWJGCSudt1j5CV .icon-shape,#mermaid-svg-uhwWJGCSudt1j5CV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uhwWJGCSudt1j5CV .icon-shape p,#mermaid-svg-uhwWJGCSudt1j5CV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uhwWJGCSudt1j5CV .icon-shape .label rect,#mermaid-svg-uhwWJGCSudt1j5CV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uhwWJGCSudt1j5CV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uhwWJGCSudt1j5CV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uhwWJGCSudt1j5CV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
WatchList 开始
10s Ticker 启动
事件循环
收到事件
observeLastEventTimeStamp()
收到 Bookmark
(InitialEventsAnnotation)?
退出循环
Stop Ticker
Ticker 触发
且距上次事件 > 10s?
输出警告日志
hasn't received required bookmark
设计意图 :WatchList 模式下,API Server 应在初始事件流结束时发送带 k8s.io/initial-events-end 注解的 Bookmark。如果 10s 内未收到任何事件,或收到事件后 10s 内未收到 Bookmark,输出警告。这是 CI 调试辅助工具,不影响生产逻辑。
4.2 VeryShortWatchError --- 极短 Watch 检测
go
type VeryShortWatchError struct {
Name string
}
func (e *VeryShortWatchError) Error() string {
return fmt.Sprintf("very short watch: %s: Unexpected watch close - "+
"watch lasted less than a second and no items received", e.Name)
}
触发条件:Watch 持续时间 < 1s 且收到 0 个事件。
设计意图:正常 Watch 不应在 1s 内无事件关闭。这种情况通常意味着网络问题或 API Server 异常,应触发 backoff 重试而非静默忽略。
4.3 isUnsupportedTableObject / isUnsupportedTableListObject --- Table 格式过滤
go
var unsupportedTableGVK = map[schema.GroupVersionKind]bool{
metav1beta1.SchemeGroupVersion.WithKind("Table"): true,
metav1.SchemeGroupVersion.WithKind("Table"): true,
}
设计意图:kubectl 等客户端可能请求 Table 格式的输出,但 Informer 不应接收 Table 对象。如果 Watch 返回了 Table 类型的事件,直接跳过并记录错误。
五、Reflector 构造方式对比
| 构造函数 | 适用场景 | 特点 |
|---|---|---|
NewReflector(lw, et, store, resync) |
简单场景 | 默认名称从调用栈推导 |
NewNamedReflector(name, lw, et, store, resync) |
需要指定名称 | 手动指定 name |
NewReflectorWithOptions(lw, et, store, opts) |
完整配置 | 支持所有选项 |
NewNamespaceKeyedIndexerAndReflector(lw, et, resync) |
快捷创建 | 同时创建 Indexer + Reflector,Indexer 按 Namespace 索引 |
所有构造最终都调用 NewReflectorWithOptions:
#mermaid-svg-0G19s5paipqt66ya{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-0G19s5paipqt66ya .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0G19s5paipqt66ya .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0G19s5paipqt66ya .error-icon{fill:#552222;}#mermaid-svg-0G19s5paipqt66ya .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0G19s5paipqt66ya .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0G19s5paipqt66ya .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0G19s5paipqt66ya .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0G19s5paipqt66ya .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0G19s5paipqt66ya .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0G19s5paipqt66ya .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0G19s5paipqt66ya .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0G19s5paipqt66ya .marker.cross{stroke:#333333;}#mermaid-svg-0G19s5paipqt66ya svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0G19s5paipqt66ya p{margin:0;}#mermaid-svg-0G19s5paipqt66ya .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0G19s5paipqt66ya .cluster-label text{fill:#333;}#mermaid-svg-0G19s5paipqt66ya .cluster-label span{color:#333;}#mermaid-svg-0G19s5paipqt66ya .cluster-label span p{background-color:transparent;}#mermaid-svg-0G19s5paipqt66ya .label text,#mermaid-svg-0G19s5paipqt66ya span{fill:#333;color:#333;}#mermaid-svg-0G19s5paipqt66ya .node rect,#mermaid-svg-0G19s5paipqt66ya .node circle,#mermaid-svg-0G19s5paipqt66ya .node ellipse,#mermaid-svg-0G19s5paipqt66ya .node polygon,#mermaid-svg-0G19s5paipqt66ya .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0G19s5paipqt66ya .rough-node .label text,#mermaid-svg-0G19s5paipqt66ya .node .label text,#mermaid-svg-0G19s5paipqt66ya .image-shape .label,#mermaid-svg-0G19s5paipqt66ya .icon-shape .label{text-anchor:middle;}#mermaid-svg-0G19s5paipqt66ya .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0G19s5paipqt66ya .rough-node .label,#mermaid-svg-0G19s5paipqt66ya .node .label,#mermaid-svg-0G19s5paipqt66ya .image-shape .label,#mermaid-svg-0G19s5paipqt66ya .icon-shape .label{text-align:center;}#mermaid-svg-0G19s5paipqt66ya .node.clickable{cursor:pointer;}#mermaid-svg-0G19s5paipqt66ya .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0G19s5paipqt66ya .arrowheadPath{fill:#333333;}#mermaid-svg-0G19s5paipqt66ya .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0G19s5paipqt66ya .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0G19s5paipqt66ya .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0G19s5paipqt66ya .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0G19s5paipqt66ya .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0G19s5paipqt66ya .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0G19s5paipqt66ya .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0G19s5paipqt66ya .cluster text{fill:#333;}#mermaid-svg-0G19s5paipqt66ya .cluster span{color:#333;}#mermaid-svg-0G19s5paipqt66ya 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-0G19s5paipqt66ya .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0G19s5paipqt66ya rect.text{fill:none;stroke-width:0;}#mermaid-svg-0G19s5paipqt66ya .icon-shape,#mermaid-svg-0G19s5paipqt66ya .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0G19s5paipqt66ya .icon-shape p,#mermaid-svg-0G19s5paipqt66ya .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0G19s5paipqt66ya .icon-shape .label rect,#mermaid-svg-0G19s5paipqt66ya .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0G19s5paipqt66ya .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0G19s5paipqt66ya .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0G19s5paipqt66ya :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} NewReflector()
NewNamedReflector()
NewNamespaceKeyedIndexerAndReflector()
ReflectorOptions{ResyncPeriod}
ReflectorOptions{Name, ResyncPeriod}
NewIndexer(MetaNamespaceKeyFunc,
Indexers{NamespaceIndex: ...})
NewReflectorWithOptions()