Part 4: kube-apiserver Storage/ETCD 与 Watch 机制超深度分析
一、模块定位:Storage 层与 Watch 机制业务职责
1.1 Storage 层的定位
在 kube-apiserver 的分层架构中,Storage 层处于 Registry 层与底层 etcd 之间,承担着以下核心职责:
| 职责 | 说明 |
|---|---|
| 数据持久化 | 将 Kubernetes API 对象序列化后写入 etcd,是所有资源唯一的持久化途径 |
| 一致性保证 | 基于 etcd 的 MVCC 机制实现乐观并发控制,保证并发写操作的线性一致性 |
| 版本管理 | 管理对象的 ResourceVersion,实现 Read-After-Write 一致性与 Watch 断点续传 |
| Watch 事件分发 | 将 etcd 的变更事件经过解码、过滤后分发给所有活跃的 Watcher |
| 缓存加速 | 通过 Cacher 在内存中维护反射缓存,减少 etcd 读取压力,加速 List/Watch |
| 加密转换 | 通过 Transformer 对写入 etcd 的数据进行加密,读取时解密 |
| 分页查询 | 支持大结果集的分页返回,避免 etcd 和 apiserver 内存溢出 |
1.2 Watch 机制的定位
Watch 是 Kubernetes 声明式 API 的核心基石:
- Informers 的数据源:所有控制器(kube-controller-manager、scheduler 等)通过 Informer 的 List+Watch 机制获取对象变更
- kubectl get -w 的底层 :用户通过
kubectl get pods -w实时观察资源变化 - 一致性保证的关键:Watch 保证客户端不会遗漏任何变更,通过 ResourceVersion 实现断点续传
- 系统扩展性的基础:Watch 机制使控制器可以被动感知变更而非轮询,极大降低 apiserver 负载
1.3 整体调用链路
HTTP Request → Handler → Registry Store → Cacher (缓存层) → ETCD3 Store → etcd server
↓ ↓
cacheWatcher etcd3 watcher
↓ ↓
resultChan → HTTP Chunked Response → Client
二、模块整体结构
2.1 Storage 接口层次(Interface/Cache/Watcher)
Kubernetes Storage 层采用经典的 接口分层 + 装饰器 模式:
#mermaid-svg-pIIpKPUYumEBrES3{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-pIIpKPUYumEBrES3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pIIpKPUYumEBrES3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pIIpKPUYumEBrES3 .error-icon{fill:#552222;}#mermaid-svg-pIIpKPUYumEBrES3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pIIpKPUYumEBrES3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pIIpKPUYumEBrES3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pIIpKPUYumEBrES3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pIIpKPUYumEBrES3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pIIpKPUYumEBrES3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pIIpKPUYumEBrES3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pIIpKPUYumEBrES3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pIIpKPUYumEBrES3 .marker.cross{stroke:#333333;}#mermaid-svg-pIIpKPUYumEBrES3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pIIpKPUYumEBrES3 p{margin:0;}#mermaid-svg-pIIpKPUYumEBrES3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pIIpKPUYumEBrES3 .cluster-label text{fill:#333;}#mermaid-svg-pIIpKPUYumEBrES3 .cluster-label span{color:#333;}#mermaid-svg-pIIpKPUYumEBrES3 .cluster-label span p{background-color:transparent;}#mermaid-svg-pIIpKPUYumEBrES3 .label text,#mermaid-svg-pIIpKPUYumEBrES3 span{fill:#333;color:#333;}#mermaid-svg-pIIpKPUYumEBrES3 .node rect,#mermaid-svg-pIIpKPUYumEBrES3 .node circle,#mermaid-svg-pIIpKPUYumEBrES3 .node ellipse,#mermaid-svg-pIIpKPUYumEBrES3 .node polygon,#mermaid-svg-pIIpKPUYumEBrES3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pIIpKPUYumEBrES3 .rough-node .label text,#mermaid-svg-pIIpKPUYumEBrES3 .node .label text,#mermaid-svg-pIIpKPUYumEBrES3 .image-shape .label,#mermaid-svg-pIIpKPUYumEBrES3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-pIIpKPUYumEBrES3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pIIpKPUYumEBrES3 .rough-node .label,#mermaid-svg-pIIpKPUYumEBrES3 .node .label,#mermaid-svg-pIIpKPUYumEBrES3 .image-shape .label,#mermaid-svg-pIIpKPUYumEBrES3 .icon-shape .label{text-align:center;}#mermaid-svg-pIIpKPUYumEBrES3 .node.clickable{cursor:pointer;}#mermaid-svg-pIIpKPUYumEBrES3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pIIpKPUYumEBrES3 .arrowheadPath{fill:#333333;}#mermaid-svg-pIIpKPUYumEBrES3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pIIpKPUYumEBrES3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pIIpKPUYumEBrES3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pIIpKPUYumEBrES3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pIIpKPUYumEBrES3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pIIpKPUYumEBrES3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pIIpKPUYumEBrES3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pIIpKPUYumEBrES3 .cluster text{fill:#333;}#mermaid-svg-pIIpKPUYumEBrES3 .cluster span{color:#333;}#mermaid-svg-pIIpKPUYumEBrES3 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-pIIpKPUYumEBrES3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pIIpKPUYumEBrES3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-pIIpKPUYumEBrES3 .icon-shape,#mermaid-svg-pIIpKPUYumEBrES3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pIIpKPUYumEBrES3 .icon-shape p,#mermaid-svg-pIIpKPUYumEBrES3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pIIpKPUYumEBrES3 .icon-shape .label rect,#mermaid-svg-pIIpKPUYumEBrES3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pIIpKPUYumEBrES3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pIIpKPUYumEBrES3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pIIpKPUYumEBrES3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Storage 接口层次
委托写操作
Watch/Get/List
Watch 接口
watch.Interface
统一 Watch 接口
watchChan
etcd3 原生 Watcher
cacheWatcher
Cacher 层 Watcher
storage.Interface
统一存储接口
etcd3.store
etcd 直接实现
Cacher
带缓存的装饰器
watchCache
内存缓存
2.1.1 storage.Interface 核心接口定义
storage.Interface 定义于 interfaces.go,是整个 Storage 层的顶层抽象:
go
type Interface interface {
Versioner() Versioner
Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error
Delete(ctx context.Context, key string, out runtime.Object, preconditions *Preconditions,
validateDeletion ValidateObjectFunc, cachedExistingObject runtime.Object) error
Watch(ctx context.Context, key string, opts ListOptions) (watch.Interface, error)
WatchList(ctx context.Context, key string, opts ListOptions) (watch.Interface, error)
Get(ctx context.Context, key string, opts GetOptions, objPtr runtime.Object) error
GetToList(ctx context.Context, key string, opts ListOptions, listObj runtime.Object) error
List(ctx context.Context, key string, opts ListOptions, listObj runtime.Object) error
GuaranteedUpdate(ctx context.Context, key string, ptrToType runtime.Object, ignoreNotFound bool,
preconditions *Preconditions, tryUpdate UpdateFunc, cachedExistingObject runtime.Object) error
Count(key string) (int64, error)
}
关键设计原则:
- 接口统一 :无论底层是 etcd3 还是 Cacher 装饰器,上层 Registry 只依赖
storage.Interface - Watch/WatchList 分离:Watch 监听单个 key,WatchList 监听 key 前缀下所有对象
- Preconditions 机制:通过 UID/ResourceVersion 前置条件防止误操作
- cachedExistingObject 优化:允许调用方提供已知的当前对象,避免额外的 Get 读取
2.1.2 Versioner 接口
Versioner 负责 ResourceVersion 在存储层和 API 对象之间的双向转换:
go
type Versioner interface {
UpdateObject(obj runtime.Object, resourceVersion uint64) error
UpdateList(obj runtime.Object, resourceVersion uint64, continueValue string, remainingItemCount *int64) error
PrepareObjectForStorage(obj runtime.Object) error
ObjectResourceVersion(obj runtime.Object) (uint64, error)
ParseResourceVersion(resourceVersion string) (uint64, error)
}
UpdateObject:将 etcd 的 ModRevision 写入对象的 ResourceVersion 字段ParseResourceVersion:将用户传入的 string 格式 RV 解析为 uint64(对应 etcd revision)PrepareObjectForStorage:写入前清空 SelfLink 和 ResourceVersion(不存储这两个字段)
2.1.3 GetOptions 与 ListOptions
go
type GetOptions struct {
IgnoreNotFound bool
ResourceVersion string // "不早于"约束
}
type ListOptions struct {
ResourceVersion string
ResourceVersionMatch metav1.ResourceVersionMatch // Exact | NotOlderThan
Predicate SelectionPredicate
ProgressNotify bool // 是否接收 Bookmark 事件
}
ResourceVersionMatch 语义:
- 空字符串(legacy):RV>0 时按 Exact 语义,RV="" 时取最新
- NotOlderThan:返回数据至少与指定 RV 一样新,尽量取最新
- Exact:必须返回指定 RV 版本的数据
2.2 Cacher 结构体
渲染错误: Mermaid 渲染失败: Parse error on line 18: ... +stopCh chan struct{} +dispatch -----------------------^ Expecting 'STRUCT_STOP', 'MEMBER', got 'OPEN_IN_STRUCT'
Cacher 的核心设计思想是 装饰器模式:
- 写操作(Create/Delete/GuaranteedUpdate) :直接委托给底层
storage.Interface(etcd3 store),Cacher 不拦截写 - 读操作(Get/List) :优先从
watchCache内存缓存读取,减少 etcd 访问 - Watch 操作 :在 Cacher 层创建
cacheWatcher,从watchCache获取初始事件后,通过incomingchannel 接收后续事件
2.3 Registry Store 结构体
#mermaid-svg-7RICpol80L2IEGIW{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-7RICpol80L2IEGIW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7RICpol80L2IEGIW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7RICpol80L2IEGIW .error-icon{fill:#552222;}#mermaid-svg-7RICpol80L2IEGIW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7RICpol80L2IEGIW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7RICpol80L2IEGIW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7RICpol80L2IEGIW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7RICpol80L2IEGIW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7RICpol80L2IEGIW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7RICpol80L2IEGIW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7RICpol80L2IEGIW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7RICpol80L2IEGIW .marker.cross{stroke:#333333;}#mermaid-svg-7RICpol80L2IEGIW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7RICpol80L2IEGIW p{margin:0;}#mermaid-svg-7RICpol80L2IEGIW g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-7RICpol80L2IEGIW g.classGroup text .title{font-weight:bolder;}#mermaid-svg-7RICpol80L2IEGIW .cluster-label text{fill:#333;}#mermaid-svg-7RICpol80L2IEGIW .cluster-label span{color:#333;}#mermaid-svg-7RICpol80L2IEGIW .cluster-label span p{background-color:transparent;}#mermaid-svg-7RICpol80L2IEGIW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7RICpol80L2IEGIW .cluster text{fill:#333;}#mermaid-svg-7RICpol80L2IEGIW .cluster span{color:#333;}#mermaid-svg-7RICpol80L2IEGIW .nodeLabel,#mermaid-svg-7RICpol80L2IEGIW .edgeLabel{color:#131300;}#mermaid-svg-7RICpol80L2IEGIW .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-7RICpol80L2IEGIW .label text{fill:#131300;}#mermaid-svg-7RICpol80L2IEGIW .labelBkg{background:#ECECFF;}#mermaid-svg-7RICpol80L2IEGIW .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-7RICpol80L2IEGIW .classTitle{font-weight:bolder;}#mermaid-svg-7RICpol80L2IEGIW .node rect,#mermaid-svg-7RICpol80L2IEGIW .node circle,#mermaid-svg-7RICpol80L2IEGIW .node ellipse,#mermaid-svg-7RICpol80L2IEGIW .node polygon,#mermaid-svg-7RICpol80L2IEGIW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7RICpol80L2IEGIW .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-7RICpol80L2IEGIW g.clickable{cursor:pointer;}#mermaid-svg-7RICpol80L2IEGIW g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-7RICpol80L2IEGIW g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-7RICpol80L2IEGIW .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-7RICpol80L2IEGIW .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-7RICpol80L2IEGIW .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-7RICpol80L2IEGIW .dashed-line{stroke-dasharray:3;}#mermaid-svg-7RICpol80L2IEGIW .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-7RICpol80L2IEGIW #compositionStart,#mermaid-svg-7RICpol80L2IEGIW .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7RICpol80L2IEGIW #compositionEnd,#mermaid-svg-7RICpol80L2IEGIW .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7RICpol80L2IEGIW #dependencyStart,#mermaid-svg-7RICpol80L2IEGIW .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7RICpol80L2IEGIW #dependencyStart,#mermaid-svg-7RICpol80L2IEGIW .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7RICpol80L2IEGIW #extensionStart,#mermaid-svg-7RICpol80L2IEGIW .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7RICpol80L2IEGIW #extensionEnd,#mermaid-svg-7RICpol80L2IEGIW .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7RICpol80L2IEGIW #aggregationStart,#mermaid-svg-7RICpol80L2IEGIW .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7RICpol80L2IEGIW #aggregationEnd,#mermaid-svg-7RICpol80L2IEGIW .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7RICpol80L2IEGIW #lollipopStart,#mermaid-svg-7RICpol80L2IEGIW .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7RICpol80L2IEGIW #lollipopEnd,#mermaid-svg-7RICpol80L2IEGIW .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7RICpol80L2IEGIW .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-7RICpol80L2IEGIW .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7RICpol80L2IEGIW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7RICpol80L2IEGIW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7RICpol80L2IEGIW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Store
+DefaultQualifiedResource schema.GroupResource
+EnableGarbageCollection bool
+DeleteCollectionWorkers int
+CreateStrategy RESTCreateStrategy
+BeginCreate BeginCreateFunc
+AfterCreate AfterCreateFunc
+UpdateStrategy RESTUpdateStrategy
+BeginUpdate BeginUpdateFunc
+AfterUpdate AfterUpdateFunc
+DeleteStrategy RESTDeleteStrategy
+AfterDelete AfterDeleteFunc
+ReturnDeletedObject bool
+TableConvertor rest.TableConvertor
+ResetFieldsStrategy rest.ResetFieldsStrategy
+Storage DryRunnableStorage
+StorageVersioner runtime.GroupVersioner
+NewFunc func() : runtime.Object
+NewListFunc func() : runtime.Object
+KeyRootFunc func(ctx) : string
+KeyFunc func(ctx, name)(string, error)
+ObjectNameFunc func(obj)(string, error)
+TTLFunc func(obj, existing, update)(uint64, error)
+PredicateFunc func(label, field) : SelectionPredicate
+Decorator func(runtime.Object)
+ShouldDeleteDuringUpdate func(ctx, key, obj, existing) : bool
+DestroyFunc func()
+Create(ctx, obj, createValidation, options)(runtime.Object, error)
+Update(ctx, name, objInfo, createValidation, updateValidation, forceAllowCreate, options)(runtime.Object, bool, error)
+Get(ctx, name, options)(runtime.Object, error)
+List(ctx, options)(runtime.Object, error)
+Delete(ctx, name, deleteValidation, options)(runtime.Object, bool, error)
+Watch(ctx, options)(watch.Interface, error)
Registry Store 是 业务层与存储层的桥梁:
- 封装了 Strategy 模式的 CRUD 逻辑
- 管理 KeyFunc(将对象名映射为 etcd key)
- 协调 Admission、Validation、Strategy、Storage 的执行顺序
- 处理 Finalizer、Graceful Deletion、GC 等复杂业务逻辑
2.4 RESTStrategy 接口
#mermaid-svg-mIgThijgLAAFoF90{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-mIgThijgLAAFoF90 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mIgThijgLAAFoF90 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mIgThijgLAAFoF90 .error-icon{fill:#552222;}#mermaid-svg-mIgThijgLAAFoF90 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mIgThijgLAAFoF90 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mIgThijgLAAFoF90 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mIgThijgLAAFoF90 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mIgThijgLAAFoF90 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mIgThijgLAAFoF90 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mIgThijgLAAFoF90 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mIgThijgLAAFoF90 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mIgThijgLAAFoF90 .marker.cross{stroke:#333333;}#mermaid-svg-mIgThijgLAAFoF90 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mIgThijgLAAFoF90 p{margin:0;}#mermaid-svg-mIgThijgLAAFoF90 g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-mIgThijgLAAFoF90 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-mIgThijgLAAFoF90 .cluster-label text{fill:#333;}#mermaid-svg-mIgThijgLAAFoF90 .cluster-label span{color:#333;}#mermaid-svg-mIgThijgLAAFoF90 .cluster-label span p{background-color:transparent;}#mermaid-svg-mIgThijgLAAFoF90 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mIgThijgLAAFoF90 .cluster text{fill:#333;}#mermaid-svg-mIgThijgLAAFoF90 .cluster span{color:#333;}#mermaid-svg-mIgThijgLAAFoF90 .nodeLabel,#mermaid-svg-mIgThijgLAAFoF90 .edgeLabel{color:#131300;}#mermaid-svg-mIgThijgLAAFoF90 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-mIgThijgLAAFoF90 .label text{fill:#131300;}#mermaid-svg-mIgThijgLAAFoF90 .labelBkg{background:#ECECFF;}#mermaid-svg-mIgThijgLAAFoF90 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-mIgThijgLAAFoF90 .classTitle{font-weight:bolder;}#mermaid-svg-mIgThijgLAAFoF90 .node rect,#mermaid-svg-mIgThijgLAAFoF90 .node circle,#mermaid-svg-mIgThijgLAAFoF90 .node ellipse,#mermaid-svg-mIgThijgLAAFoF90 .node polygon,#mermaid-svg-mIgThijgLAAFoF90 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mIgThijgLAAFoF90 .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-mIgThijgLAAFoF90 g.clickable{cursor:pointer;}#mermaid-svg-mIgThijgLAAFoF90 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-mIgThijgLAAFoF90 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-mIgThijgLAAFoF90 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-mIgThijgLAAFoF90 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-mIgThijgLAAFoF90 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-mIgThijgLAAFoF90 .dashed-line{stroke-dasharray:3;}#mermaid-svg-mIgThijgLAAFoF90 .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-mIgThijgLAAFoF90 #compositionStart,#mermaid-svg-mIgThijgLAAFoF90 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-mIgThijgLAAFoF90 #compositionEnd,#mermaid-svg-mIgThijgLAAFoF90 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-mIgThijgLAAFoF90 #dependencyStart,#mermaid-svg-mIgThijgLAAFoF90 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-mIgThijgLAAFoF90 #dependencyStart,#mermaid-svg-mIgThijgLAAFoF90 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-mIgThijgLAAFoF90 #extensionStart,#mermaid-svg-mIgThijgLAAFoF90 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-mIgThijgLAAFoF90 #extensionEnd,#mermaid-svg-mIgThijgLAAFoF90 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-mIgThijgLAAFoF90 #aggregationStart,#mermaid-svg-mIgThijgLAAFoF90 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-mIgThijgLAAFoF90 #aggregationEnd,#mermaid-svg-mIgThijgLAAFoF90 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-mIgThijgLAAFoF90 #lollipopStart,#mermaid-svg-mIgThijgLAAFoF90 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-mIgThijgLAAFoF90 #lollipopEnd,#mermaid-svg-mIgThijgLAAFoF90 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-mIgThijgLAAFoF90 .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-mIgThijgLAAFoF90 .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mIgThijgLAAFoF90 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mIgThijgLAAFoF90 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mIgThijgLAAFoF90 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} <<interface>>
RESTCreateStrategy
+NamespaceScoped() : bool
+PrepareForCreate(ctx, obj)
+Validate(ctx, obj) : field.ErrorList
+Canonicalize(obj)
+GenerateName(base string) : string
<<interface>>
RESTUpdateStrategy
+NamespaceScoped() : bool
+AllowCreateOnUpdate() : bool
+PrepareForUpdate(ctx, obj, old)
+ValidateUpdate(ctx, obj, old) : field.ErrorList
+Canonicalize(obj)
+AllowUnconditionalUpdate() : bool
<<interface>>
RESTDeleteStrategy
+ObjectTyper methods
<<interface>>
GarbageCollectionDeleteStrategy
+DefaultGarbageCollectionPolicy(ctx) : GarbageCollectionPolicy
<<interface>>
RESTGracefulDeleteStrategy
+CheckGracefulDelete(ctx, obj, options) : bool
Strategy 模式是 Registry 层最关键的设计:
- 每个资源类型(Pod、Service、ConfigMap 等)都实现自己的 Strategy
- PrepareForCreate/PrepareForUpdate:在持久化前对对象做规范化(如清除 status 字段)
- Validate/ValidateUpdate:业务语义校验
- Canonicalize:最终标准化(如排序 labels)
- AllowCreateOnUpdate:控制 PUT 是否能创建新对象
- AllowUnconditionalUpdate:控制是否允许不带 ResourceVersion 的更新
三、核心业务逻辑深度解析
3.1 ETCD3 Store Create 逐行解析
#mermaid-svg-mvMrACzMyM5MKplI{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-mvMrACzMyM5MKplI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mvMrACzMyM5MKplI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mvMrACzMyM5MKplI .error-icon{fill:#552222;}#mermaid-svg-mvMrACzMyM5MKplI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mvMrACzMyM5MKplI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mvMrACzMyM5MKplI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mvMrACzMyM5MKplI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mvMrACzMyM5MKplI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mvMrACzMyM5MKplI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mvMrACzMyM5MKplI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mvMrACzMyM5MKplI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mvMrACzMyM5MKplI .marker.cross{stroke:#333333;}#mermaid-svg-mvMrACzMyM5MKplI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mvMrACzMyM5MKplI p{margin:0;}#mermaid-svg-mvMrACzMyM5MKplI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mvMrACzMyM5MKplI .cluster-label text{fill:#333;}#mermaid-svg-mvMrACzMyM5MKplI .cluster-label span{color:#333;}#mermaid-svg-mvMrACzMyM5MKplI .cluster-label span p{background-color:transparent;}#mermaid-svg-mvMrACzMyM5MKplI .label text,#mermaid-svg-mvMrACzMyM5MKplI span{fill:#333;color:#333;}#mermaid-svg-mvMrACzMyM5MKplI .node rect,#mermaid-svg-mvMrACzMyM5MKplI .node circle,#mermaid-svg-mvMrACzMyM5MKplI .node ellipse,#mermaid-svg-mvMrACzMyM5MKplI .node polygon,#mermaid-svg-mvMrACzMyM5MKplI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mvMrACzMyM5MKplI .rough-node .label text,#mermaid-svg-mvMrACzMyM5MKplI .node .label text,#mermaid-svg-mvMrACzMyM5MKplI .image-shape .label,#mermaid-svg-mvMrACzMyM5MKplI .icon-shape .label{text-anchor:middle;}#mermaid-svg-mvMrACzMyM5MKplI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mvMrACzMyM5MKplI .rough-node .label,#mermaid-svg-mvMrACzMyM5MKplI .node .label,#mermaid-svg-mvMrACzMyM5MKplI .image-shape .label,#mermaid-svg-mvMrACzMyM5MKplI .icon-shape .label{text-align:center;}#mermaid-svg-mvMrACzMyM5MKplI .node.clickable{cursor:pointer;}#mermaid-svg-mvMrACzMyM5MKplI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mvMrACzMyM5MKplI .arrowheadPath{fill:#333333;}#mermaid-svg-mvMrACzMyM5MKplI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mvMrACzMyM5MKplI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mvMrACzMyM5MKplI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mvMrACzMyM5MKplI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mvMrACzMyM5MKplI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mvMrACzMyM5MKplI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mvMrACzMyM5MKplI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mvMrACzMyM5MKplI .cluster text{fill:#333;}#mermaid-svg-mvMrACzMyM5MKplI .cluster span{color:#333;}#mermaid-svg-mvMrACzMyM5MKplI 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-mvMrACzMyM5MKplI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mvMrACzMyM5MKplI rect.text{fill:none;stroke-width:0;}#mermaid-svg-mvMrACzMyM5MKplI .icon-shape,#mermaid-svg-mvMrACzMyM5MKplI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mvMrACzMyM5MKplI .icon-shape p,#mermaid-svg-mvMrACzMyM5MKplI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mvMrACzMyM5MKplI .icon-shape .label rect,#mermaid-svg-mvMrACzMyM5MKplI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mvMrACzMyM5MKplI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mvMrACzMyM5MKplI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mvMrACzMyM5MKplI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
否
是
是
否
Create 入口
prepareKey: 拼接 pathPrefix + key
路径校验防穿越
obj.ResourceVersion != 0?
返回错误: 新对象不应带 RV
PrepareObjectForStorage
清空 SelfLink/RV
runtime.Encode: 序列化对象
TransformToStorage: 加密转换
ttlOpts: TTL→Lease 映射
Txn 事务:
If: ModRevision key == 0
Then: OpPut
Else: 无
txnResp.Succeeded?
返回 KeyExistsError
out != nil?
decode: 反序列化写入结果
设置新 Revision 为 RV
返回 nil 成功
核心机制解析:
-
原子性创建 :使用 etcd Txn 事务的
If(ModRevision(key) == 0)确保只有 key 不存在时才写入。ModRevision 为 0 意味着 key 从未存在或已被删除,这是 etcd 的原子 Compare-And-Create 操作。 -
加密转换 :
TransformToStorage对数据做加密(如果配置了 KMS),authenticatedDataString(key)将 etcd key 作为认证数据绑定,防止密钥在不同 key 间被复用。 -
TTL 到 Lease 的映射 :etcd3 使用 Lease 而非 TTL,
leaseManager.GetLease将 TTL 秒数映射到合适的 Lease ID,相同 TTL 的对象共享 Lease 减少 etcd 负担。 -
ResourceVersion 清零:创建时强制要求 obj.ResourceVersion == 0,防止客户端通过创建操作"复活"已删除对象的旧版本。
3.2 ETCD3 Store Get/List 逐行解析
Get 流程
#mermaid-svg-xkx7KlvKOJoLhgcO{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-xkx7KlvKOJoLhgcO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xkx7KlvKOJoLhgcO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xkx7KlvKOJoLhgcO .error-icon{fill:#552222;}#mermaid-svg-xkx7KlvKOJoLhgcO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xkx7KlvKOJoLhgcO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xkx7KlvKOJoLhgcO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xkx7KlvKOJoLhgcO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xkx7KlvKOJoLhgcO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xkx7KlvKOJoLhgcO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xkx7KlvKOJoLhgcO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xkx7KlvKOJoLhgcO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xkx7KlvKOJoLhgcO .marker.cross{stroke:#333333;}#mermaid-svg-xkx7KlvKOJoLhgcO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xkx7KlvKOJoLhgcO p{margin:0;}#mermaid-svg-xkx7KlvKOJoLhgcO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xkx7KlvKOJoLhgcO .cluster-label text{fill:#333;}#mermaid-svg-xkx7KlvKOJoLhgcO .cluster-label span{color:#333;}#mermaid-svg-xkx7KlvKOJoLhgcO .cluster-label span p{background-color:transparent;}#mermaid-svg-xkx7KlvKOJoLhgcO .label text,#mermaid-svg-xkx7KlvKOJoLhgcO span{fill:#333;color:#333;}#mermaid-svg-xkx7KlvKOJoLhgcO .node rect,#mermaid-svg-xkx7KlvKOJoLhgcO .node circle,#mermaid-svg-xkx7KlvKOJoLhgcO .node ellipse,#mermaid-svg-xkx7KlvKOJoLhgcO .node polygon,#mermaid-svg-xkx7KlvKOJoLhgcO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xkx7KlvKOJoLhgcO .rough-node .label text,#mermaid-svg-xkx7KlvKOJoLhgcO .node .label text,#mermaid-svg-xkx7KlvKOJoLhgcO .image-shape .label,#mermaid-svg-xkx7KlvKOJoLhgcO .icon-shape .label{text-anchor:middle;}#mermaid-svg-xkx7KlvKOJoLhgcO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xkx7KlvKOJoLhgcO .rough-node .label,#mermaid-svg-xkx7KlvKOJoLhgcO .node .label,#mermaid-svg-xkx7KlvKOJoLhgcO .image-shape .label,#mermaid-svg-xkx7KlvKOJoLhgcO .icon-shape .label{text-align:center;}#mermaid-svg-xkx7KlvKOJoLhgcO .node.clickable{cursor:pointer;}#mermaid-svg-xkx7KlvKOJoLhgcO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xkx7KlvKOJoLhgcO .arrowheadPath{fill:#333333;}#mermaid-svg-xkx7KlvKOJoLhgcO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xkx7KlvKOJoLhgcO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xkx7KlvKOJoLhgcO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xkx7KlvKOJoLhgcO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xkx7KlvKOJoLhgcO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xkx7KlvKOJoLhgcO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xkx7KlvKOJoLhgcO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xkx7KlvKOJoLhgcO .cluster text{fill:#333;}#mermaid-svg-xkx7KlvKOJoLhgcO .cluster span{color:#333;}#mermaid-svg-xkx7KlvKOJoLhgcO 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-xkx7KlvKOJoLhgcO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xkx7KlvKOJoLhgcO rect.text{fill:none;stroke-width:0;}#mermaid-svg-xkx7KlvKOJoLhgcO .icon-shape,#mermaid-svg-xkx7KlvKOJoLhgcO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xkx7KlvKOJoLhgcO .icon-shape p,#mermaid-svg-xkx7KlvKOJoLhgcO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xkx7KlvKOJoLhgcO .icon-shape .label rect,#mermaid-svg-xkx7KlvKOJoLhgcO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xkx7KlvKOJoLhgcO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xkx7KlvKOJoLhgcO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xkx7KlvKOJoLhgcO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
是
否
否
Get 入口
prepareKey
client.KV.Get ctx, key
validateMinimumResourceVersion
检查返回数据是否满足最低 RV 要求
len Kvs == 0?
IgnoreNotFound?
SetZeroValue 返回零值
返回 KeyNotFoundError
TransformFromStorage: 解密
decode: 反序列化 + 设置 ModRevision 为 RV
validateMinimumResourceVersion 逻辑:
- 如果请求指定了 RV,etcd 返回的 Header.Revision 必须 >= 请求的 RV
- 否则返回
TooLargeResourceVersionError,提示客户端稍后重试 - 这是 "not older than" 语义的保证
List 流程
#mermaid-svg-80PqXGfrdJm5i0PW{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-80PqXGfrdJm5i0PW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-80PqXGfrdJm5i0PW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-80PqXGfrdJm5i0PW .error-icon{fill:#552222;}#mermaid-svg-80PqXGfrdJm5i0PW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-80PqXGfrdJm5i0PW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-80PqXGfrdJm5i0PW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-80PqXGfrdJm5i0PW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-80PqXGfrdJm5i0PW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-80PqXGfrdJm5i0PW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-80PqXGfrdJm5i0PW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-80PqXGfrdJm5i0PW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-80PqXGfrdJm5i0PW .marker.cross{stroke:#333333;}#mermaid-svg-80PqXGfrdJm5i0PW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-80PqXGfrdJm5i0PW p{margin:0;}#mermaid-svg-80PqXGfrdJm5i0PW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-80PqXGfrdJm5i0PW .cluster-label text{fill:#333;}#mermaid-svg-80PqXGfrdJm5i0PW .cluster-label span{color:#333;}#mermaid-svg-80PqXGfrdJm5i0PW .cluster-label span p{background-color:transparent;}#mermaid-svg-80PqXGfrdJm5i0PW .label text,#mermaid-svg-80PqXGfrdJm5i0PW span{fill:#333;color:#333;}#mermaid-svg-80PqXGfrdJm5i0PW .node rect,#mermaid-svg-80PqXGfrdJm5i0PW .node circle,#mermaid-svg-80PqXGfrdJm5i0PW .node ellipse,#mermaid-svg-80PqXGfrdJm5i0PW .node polygon,#mermaid-svg-80PqXGfrdJm5i0PW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-80PqXGfrdJm5i0PW .rough-node .label text,#mermaid-svg-80PqXGfrdJm5i0PW .node .label text,#mermaid-svg-80PqXGfrdJm5i0PW .image-shape .label,#mermaid-svg-80PqXGfrdJm5i0PW .icon-shape .label{text-anchor:middle;}#mermaid-svg-80PqXGfrdJm5i0PW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-80PqXGfrdJm5i0PW .rough-node .label,#mermaid-svg-80PqXGfrdJm5i0PW .node .label,#mermaid-svg-80PqXGfrdJm5i0PW .image-shape .label,#mermaid-svg-80PqXGfrdJm5i0PW .icon-shape .label{text-align:center;}#mermaid-svg-80PqXGfrdJm5i0PW .node.clickable{cursor:pointer;}#mermaid-svg-80PqXGfrdJm5i0PW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-80PqXGfrdJm5i0PW .arrowheadPath{fill:#333333;}#mermaid-svg-80PqXGfrdJm5i0PW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-80PqXGfrdJm5i0PW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-80PqXGfrdJm5i0PW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-80PqXGfrdJm5i0PW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-80PqXGfrdJm5i0PW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-80PqXGfrdJm5i0PW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-80PqXGfrdJm5i0PW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-80PqXGfrdJm5i0PW .cluster text{fill:#333;}#mermaid-svg-80PqXGfrdJm5i0PW .cluster span{color:#333;}#mermaid-svg-80PqXGfrdJm5i0PW 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-80PqXGfrdJm5i0PW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-80PqXGfrdJm5i0PW rect.text{fill:none;stroke-width:0;}#mermaid-svg-80PqXGfrdJm5i0PW .icon-shape,#mermaid-svg-80PqXGfrdJm5i0PW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-80PqXGfrdJm5i0PW .icon-shape p,#mermaid-svg-80PqXGfrdJm5i0PW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-80PqXGfrdJm5i0PW .icon-shape .label rect,#mermaid-svg-80PqXGfrdJm5i0PW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-80PqXGfrdJm5i0PW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-80PqXGfrdJm5i0PW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-80PqXGfrdJm5i0PW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
Exact
NotOlderThan
legacy RV>0
legacy RV=
是
否
List 入口
prepareKey + 确保 key 以 / 结尾
分页 + 有 continue token?
decodeContinue: 解码 continue token
提取 startKey + continueRV
有 RV + ResourceVersionMatch?
设置 WithRev 选项
不设 WithRev, 取最新
设置 WithRev
不设 WithRev
循环: client.KV.Get
遍历 Kvs, TransformFromStorage 解密
appendListItem: 反序列化 + Predicate 过滤
还有更多数据 + 分页?
更新 startKey, 编码 continue token
UpdateList: 设置列表 RV + continue + remainingItemCount
List 的分页机制核心:
-
continue token 编码 :
encodeContinue将(nextKey, resourceVersion)编码为 base64 JSON,格式为{v: "meta.k8s.io/v1", rv: int64, start: string}。nextKey是上次遍历最后一个 key 加\x00(etcd key 排序中的下一个可能值)。 -
分页一致性保证 :continue token 内嵌了
resourceVersion,确保翻页时始终读取同一版本的数据快照,不会因中途变更而出现数据不一致。 -
路径遍历防护 :
decodeContinue对StartKey做了path.Clean检查,防止客户端伪造 continue token 进行路径遍历攻击。 -
Predicate 过滤 :
appendListItem对每个对象调用pred.Matches,只追加匹配的元素。对于有 selector 的查询,etcd 返回的数据远多于最终结果。
3.3 ETCD3 Store Update/Delete 逐行解析
Update(GuaranteedUpdate)
#mermaid-svg-AEVT00h0u6AxYVe2{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-AEVT00h0u6AxYVe2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AEVT00h0u6AxYVe2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AEVT00h0u6AxYVe2 .error-icon{fill:#552222;}#mermaid-svg-AEVT00h0u6AxYVe2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AEVT00h0u6AxYVe2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AEVT00h0u6AxYVe2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AEVT00h0u6AxYVe2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AEVT00h0u6AxYVe2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AEVT00h0u6AxYVe2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AEVT00h0u6AxYVe2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AEVT00h0u6AxYVe2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AEVT00h0u6AxYVe2 .marker.cross{stroke:#333333;}#mermaid-svg-AEVT00h0u6AxYVe2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AEVT00h0u6AxYVe2 p{margin:0;}#mermaid-svg-AEVT00h0u6AxYVe2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AEVT00h0u6AxYVe2 .cluster-label text{fill:#333;}#mermaid-svg-AEVT00h0u6AxYVe2 .cluster-label span{color:#333;}#mermaid-svg-AEVT00h0u6AxYVe2 .cluster-label span p{background-color:transparent;}#mermaid-svg-AEVT00h0u6AxYVe2 .label text,#mermaid-svg-AEVT00h0u6AxYVe2 span{fill:#333;color:#333;}#mermaid-svg-AEVT00h0u6AxYVe2 .node rect,#mermaid-svg-AEVT00h0u6AxYVe2 .node circle,#mermaid-svg-AEVT00h0u6AxYVe2 .node ellipse,#mermaid-svg-AEVT00h0u6AxYVe2 .node polygon,#mermaid-svg-AEVT00h0u6AxYVe2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AEVT00h0u6AxYVe2 .rough-node .label text,#mermaid-svg-AEVT00h0u6AxYVe2 .node .label text,#mermaid-svg-AEVT00h0u6AxYVe2 .image-shape .label,#mermaid-svg-AEVT00h0u6AxYVe2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-AEVT00h0u6AxYVe2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AEVT00h0u6AxYVe2 .rough-node .label,#mermaid-svg-AEVT00h0u6AxYVe2 .node .label,#mermaid-svg-AEVT00h0u6AxYVe2 .image-shape .label,#mermaid-svg-AEVT00h0u6AxYVe2 .icon-shape .label{text-align:center;}#mermaid-svg-AEVT00h0u6AxYVe2 .node.clickable{cursor:pointer;}#mermaid-svg-AEVT00h0u6AxYVe2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AEVT00h0u6AxYVe2 .arrowheadPath{fill:#333333;}#mermaid-svg-AEVT00h0u6AxYVe2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AEVT00h0u6AxYVe2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AEVT00h0u6AxYVe2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AEVT00h0u6AxYVe2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AEVT00h0u6AxYVe2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AEVT00h0u6AxYVe2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AEVT00h0u6AxYVe2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AEVT00h0u6AxYVe2 .cluster text{fill:#333;}#mermaid-svg-AEVT00h0u6AxYVe2 .cluster span{color:#333;}#mermaid-svg-AEVT00h0u6AxYVe2 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-AEVT00h0u6AxYVe2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AEVT00h0u6AxYVe2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-AEVT00h0u6AxYVe2 .icon-shape,#mermaid-svg-AEVT00h0u6AxYVe2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AEVT00h0u6AxYVe2 .icon-shape p,#mermaid-svg-AEVT00h0u6AxYVe2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AEVT00h0u6AxYVe2 .icon-shape .label rect,#mermaid-svg-AEVT00h0u6AxYVe2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AEVT00h0u6AxYVe2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AEVT00h0u6AxYVe2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AEVT00h0u6AxYVe2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
不满足 + 数据可能过期
满足
是 + 数据可能过期
否
是
否
否
是
GuaranteedUpdate 入口
prepareKey
cachedExistingObject != nil?
getStateFromObject: 从缓存对象推断状态
getCurrentState: Get 从 etcd 读取当前状态
循环开始
preconditions.Check: 检查 UID/RV 前置条件
getCurrentState 重新获取
updateState: 调用用户 tryUpdate 函数
tryUpdate 返回错误?
数据未变 + origState 不 stale?
短路返回: 无需实际写入
TransformToStorage 加密
Txn 事务:
If: ModRevision == origState.rev
Then: OpPut
Else: OpGet
txnResp.Succeeded?
从 Else 分支获取最新状态, continue 重试
decode: 反序列化结果, 设置新 RV
GuaranteedUpdate 的乐观并发控制核心:
-
CAS 循环 :
If(ModRevision(key) == origState.rev)是 CAS 操作。如果 etcd 中的 ModRevision 不等于我们读取时的 revision,说明中间有其他写入,Txn 失败,进入 Else 分支获取最新状态后重试。 -
短路优化 :如果
tryUpdate返回的对象序列化后与原始数据bytes.Equal,且 origState 不 stale,则跳过写入直接返回。这避免了对"无意义更新"的 etcd 写入。 -
stale 标记 :Transformer(加密转换器)在检测到数据用旧密钥加密时会设置
stale=true。即使数据内容不变,也需要重新写入以更新加密密钥。 -
Preconditions 双重检查:在 CAS 循环内每次迭代都检查 Preconditions。如果 Preconditions 检查失败但数据可能过期,会重新获取最新状态后重试。
Delete(conditionalDelete)
#mermaid-svg-8bBfgkFUb7Ae2wVU{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-8bBfgkFUb7Ae2wVU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8bBfgkFUb7Ae2wVU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8bBfgkFUb7Ae2wVU .error-icon{fill:#552222;}#mermaid-svg-8bBfgkFUb7Ae2wVU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8bBfgkFUb7Ae2wVU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8bBfgkFUb7Ae2wVU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8bBfgkFUb7Ae2wVU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8bBfgkFUb7Ae2wVU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8bBfgkFUb7Ae2wVU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8bBfgkFUb7Ae2wVU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8bBfgkFUb7Ae2wVU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8bBfgkFUb7Ae2wVU .marker.cross{stroke:#333333;}#mermaid-svg-8bBfgkFUb7Ae2wVU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8bBfgkFUb7Ae2wVU p{margin:0;}#mermaid-svg-8bBfgkFUb7Ae2wVU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8bBfgkFUb7Ae2wVU .cluster-label text{fill:#333;}#mermaid-svg-8bBfgkFUb7Ae2wVU .cluster-label span{color:#333;}#mermaid-svg-8bBfgkFUb7Ae2wVU .cluster-label span p{background-color:transparent;}#mermaid-svg-8bBfgkFUb7Ae2wVU .label text,#mermaid-svg-8bBfgkFUb7Ae2wVU span{fill:#333;color:#333;}#mermaid-svg-8bBfgkFUb7Ae2wVU .node rect,#mermaid-svg-8bBfgkFUb7Ae2wVU .node circle,#mermaid-svg-8bBfgkFUb7Ae2wVU .node ellipse,#mermaid-svg-8bBfgkFUb7Ae2wVU .node polygon,#mermaid-svg-8bBfgkFUb7Ae2wVU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8bBfgkFUb7Ae2wVU .rough-node .label text,#mermaid-svg-8bBfgkFUb7Ae2wVU .node .label text,#mermaid-svg-8bBfgkFUb7Ae2wVU .image-shape .label,#mermaid-svg-8bBfgkFUb7Ae2wVU .icon-shape .label{text-anchor:middle;}#mermaid-svg-8bBfgkFUb7Ae2wVU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8bBfgkFUb7Ae2wVU .rough-node .label,#mermaid-svg-8bBfgkFUb7Ae2wVU .node .label,#mermaid-svg-8bBfgkFUb7Ae2wVU .image-shape .label,#mermaid-svg-8bBfgkFUb7Ae2wVU .icon-shape .label{text-align:center;}#mermaid-svg-8bBfgkFUb7Ae2wVU .node.clickable{cursor:pointer;}#mermaid-svg-8bBfgkFUb7Ae2wVU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8bBfgkFUb7Ae2wVU .arrowheadPath{fill:#333333;}#mermaid-svg-8bBfgkFUb7Ae2wVU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8bBfgkFUb7Ae2wVU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8bBfgkFUb7Ae2wVU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8bBfgkFUb7Ae2wVU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8bBfgkFUb7Ae2wVU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8bBfgkFUb7Ae2wVU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8bBfgkFUb7Ae2wVU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8bBfgkFUb7Ae2wVU .cluster text{fill:#333;}#mermaid-svg-8bBfgkFUb7Ae2wVU .cluster span{color:#333;}#mermaid-svg-8bBfgkFUb7Ae2wVU 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-8bBfgkFUb7Ae2wVU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8bBfgkFUb7Ae2wVU rect.text{fill:none;stroke-width:0;}#mermaid-svg-8bBfgkFUb7Ae2wVU .icon-shape,#mermaid-svg-8bBfgkFUb7Ae2wVU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8bBfgkFUb7Ae2wVU .icon-shape p,#mermaid-svg-8bBfgkFUb7Ae2wVU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8bBfgkFUb7Ae2wVU .icon-shape .label rect,#mermaid-svg-8bBfgkFUb7Ae2wVU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8bBfgkFUb7Ae2wVU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8bBfgkFUb7Ae2wVU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8bBfgkFUb7Ae2wVU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
失败 + 过期
通过
失败 + 过期
通过
否
是
Delete 入口
conditionalDelete
cachedExistingObject != nil?
getStateFromObject
getCurrentState: Get from etcd
循环
preconditions.Check
getCurrentState 重试
validateDeletion 业务校验
getCurrentState 重试
Txn 事务:
If: ModRevision == origState.rev
Then: OpDelete
Else: OpGet
txnResp.Succeeded?
从 Else 获取最新, continue
decode 原始数据到 out
Delete 的核心与 GuaranteedUpdate 类似,也是 CAS 循环:
- 先检查 Preconditions(UID、ResourceVersion)
- 再执行 validateDeletion(业务层校验如 Finalizer 检查)
- 最后用 Txn 原子删除,If 条件失败则重试
注意 :Delete 返回的是 origState.data(被删除对象的数据),不是 etcd 删除响应中的数据。这是因为 etcd 删除操作不返回被删除的值(在旧版本中),Kubernetes 需要保留原始数据以便返回给客户端。
3.4 Watch 机制:ETCD Watch → Cacher → WatcherChan → 客户端完整链路
这是 Kubernetes 最复杂的子系统之一。整个链路可以分为四个阶段:
#mermaid-svg-GpuPgHR4eCDKHKIb{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-GpuPgHR4eCDKHKIb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GpuPgHR4eCDKHKIb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GpuPgHR4eCDKHKIb .error-icon{fill:#552222;}#mermaid-svg-GpuPgHR4eCDKHKIb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GpuPgHR4eCDKHKIb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GpuPgHR4eCDKHKIb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GpuPgHR4eCDKHKIb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GpuPgHR4eCDKHKIb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GpuPgHR4eCDKHKIb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GpuPgHR4eCDKHKIb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GpuPgHR4eCDKHKIb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GpuPgHR4eCDKHKIb .marker.cross{stroke:#333333;}#mermaid-svg-GpuPgHR4eCDKHKIb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GpuPgHR4eCDKHKIb p{margin:0;}#mermaid-svg-GpuPgHR4eCDKHKIb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GpuPgHR4eCDKHKIb .cluster-label text{fill:#333;}#mermaid-svg-GpuPgHR4eCDKHKIb .cluster-label span{color:#333;}#mermaid-svg-GpuPgHR4eCDKHKIb .cluster-label span p{background-color:transparent;}#mermaid-svg-GpuPgHR4eCDKHKIb .label text,#mermaid-svg-GpuPgHR4eCDKHKIb span{fill:#333;color:#333;}#mermaid-svg-GpuPgHR4eCDKHKIb .node rect,#mermaid-svg-GpuPgHR4eCDKHKIb .node circle,#mermaid-svg-GpuPgHR4eCDKHKIb .node ellipse,#mermaid-svg-GpuPgHR4eCDKHKIb .node polygon,#mermaid-svg-GpuPgHR4eCDKHKIb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GpuPgHR4eCDKHKIb .rough-node .label text,#mermaid-svg-GpuPgHR4eCDKHKIb .node .label text,#mermaid-svg-GpuPgHR4eCDKHKIb .image-shape .label,#mermaid-svg-GpuPgHR4eCDKHKIb .icon-shape .label{text-anchor:middle;}#mermaid-svg-GpuPgHR4eCDKHKIb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GpuPgHR4eCDKHKIb .rough-node .label,#mermaid-svg-GpuPgHR4eCDKHKIb .node .label,#mermaid-svg-GpuPgHR4eCDKHKIb .image-shape .label,#mermaid-svg-GpuPgHR4eCDKHKIb .icon-shape .label{text-align:center;}#mermaid-svg-GpuPgHR4eCDKHKIb .node.clickable{cursor:pointer;}#mermaid-svg-GpuPgHR4eCDKHKIb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GpuPgHR4eCDKHKIb .arrowheadPath{fill:#333333;}#mermaid-svg-GpuPgHR4eCDKHKIb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GpuPgHR4eCDKHKIb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GpuPgHR4eCDKHKIb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GpuPgHR4eCDKHKIb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GpuPgHR4eCDKHKIb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GpuPgHR4eCDKHKIb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GpuPgHR4eCDKHKIb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GpuPgHR4eCDKHKIb .cluster text{fill:#333;}#mermaid-svg-GpuPgHR4eCDKHKIb .cluster span{color:#333;}#mermaid-svg-GpuPgHR4eCDKHKIb 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-GpuPgHR4eCDKHKIb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GpuPgHR4eCDKHKIb rect.text{fill:none;stroke-width:0;}#mermaid-svg-GpuPgHR4eCDKHKIb .icon-shape,#mermaid-svg-GpuPgHR4eCDKHKIb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GpuPgHR4eCDKHKIb .icon-shape p,#mermaid-svg-GpuPgHR4eCDKHKIb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GpuPgHR4eCDKHKIb .icon-shape .label rect,#mermaid-svg-GpuPgHR4eCDKHKIb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GpuPgHR4eCDKHKIb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GpuPgHR4eCDKHKIb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GpuPgHR4eCDKHKIb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 阶段4: HTTP 响应
阶段3: cacheWatcher
阶段2: Cacher 缓存
阶段1: etcd Watch
gRPC stream
incomingEventChan
eventHandler
incoming channel
watchersBuffer
process goroutine
ResultChan
Chunked Transfer
etcd server
etcd3 watchChan
watchCache.processEvent
Cacher.processEvent
Cacher.dispatchEvents
cacheWatcher.input
cacheWatcher.result
watch.Encoder
HTTP Client
3.4.1 阶段一:ETCD3 watchChan
watcher 结构体:
go
type watcher struct {
client *clientv3.Client
codec runtime.Codec
newFunc func() runtime.Object
objectType string
versioner storage.Versioner
transformer value.Transformer
}
watchChan 结构体:
go
type watchChan struct {
watcher *watcher
key string
initialRev int64
recursive bool
progressNotify bool
internalPred storage.SelectionPredicate
ctx context.Context
cancel context.CancelFunc
incomingEventChan chan *event // 缓冲 100
resultChan chan watch.Event // 缓冲 100
errChan chan error // 缓冲 1
}
Watch 启动流程:
etcd server watchChan etcd3.watcher Registry/上层 etcd server watchChan etcd3.watcher Registry/上层 #mermaid-svg-PuEN5mvw8DuBImL5{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-PuEN5mvw8DuBImL5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PuEN5mvw8DuBImL5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PuEN5mvw8DuBImL5 .error-icon{fill:#552222;}#mermaid-svg-PuEN5mvw8DuBImL5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PuEN5mvw8DuBImL5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PuEN5mvw8DuBImL5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PuEN5mvw8DuBImL5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PuEN5mvw8DuBImL5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PuEN5mvw8DuBImL5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PuEN5mvw8DuBImL5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PuEN5mvw8DuBImL5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PuEN5mvw8DuBImL5 .marker.cross{stroke:#333333;}#mermaid-svg-PuEN5mvw8DuBImL5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PuEN5mvw8DuBImL5 p{margin:0;}#mermaid-svg-PuEN5mvw8DuBImL5 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PuEN5mvw8DuBImL5 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-PuEN5mvw8DuBImL5 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-PuEN5mvw8DuBImL5 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-PuEN5mvw8DuBImL5 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-PuEN5mvw8DuBImL5 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-PuEN5mvw8DuBImL5 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-PuEN5mvw8DuBImL5 .sequenceNumber{fill:white;}#mermaid-svg-PuEN5mvw8DuBImL5 #sequencenumber{fill:#333;}#mermaid-svg-PuEN5mvw8DuBImL5 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-PuEN5mvw8DuBImL5 .messageText{fill:#333;stroke:none;}#mermaid-svg-PuEN5mvw8DuBImL5 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PuEN5mvw8DuBImL5 .labelText,#mermaid-svg-PuEN5mvw8DuBImL5 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-PuEN5mvw8DuBImL5 .loopText,#mermaid-svg-PuEN5mvw8DuBImL5 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-PuEN5mvw8DuBImL5 .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-PuEN5mvw8DuBImL5 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-PuEN5mvw8DuBImL5 .noteText,#mermaid-svg-PuEN5mvw8DuBImL5 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-PuEN5mvw8DuBImL5 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PuEN5mvw8DuBImL5 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PuEN5mvw8DuBImL5 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PuEN5mvw8DuBImL5 .actorPopupMenu{position:absolute;}#mermaid-svg-PuEN5mvw8DuBImL5 .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-PuEN5mvw8DuBImL5 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PuEN5mvw8DuBImL5 .actor-man circle,#mermaid-svg-PuEN5mvw8DuBImL5 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-PuEN5mvw8DuBImL5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ctx = WithRequireLeader(ctx)确保连接到 leader initialRev = Header.Revision altinitialRev == 0 altIsProgressNotify正常事件 loopetcd 事件流 Watch(ctx, key, rev, recursive, progressNotify, pred)createWatchChan(ctx, key, rev, ...)go wc.run()go startWatching(watchClosedCh)go processEvent(&resultChanWG)Get(key/prefix) 获取当前对象当前数据 + Header.RevisionsendEvent 每个 kv 作为 ADDEDWatch(key, WithRev(initialRev+1), WithPrevKV, WithPrefix, WithProgressNotify)WatchResponse (events/progressNotify/error)sendEvent(progressNotifyEvent)parseEvent → sendEventincomingEventChan <- eventprocessEvent: transform + filterresultChan <- watch.Event
关键设计细节:
-
WithRequireLeader :
clientv3.WithRequireLeader(ctx)确保只有当 etcd 集群有 leader 且当前连接到 leader 时,Watch 才会工作。如果 leader 丢失,context 会被取消,Watch 优雅终止。 -
initialRev == 0 时的 sync :先 Get 当前数据,将每个对象作为 ADDED 事件发送,然后从
Header.Revision + 1开始 Watch。这保证了不会遗漏 sync 与 Watch 之间的变更。 -
WithPrevKV:请求 etcd 返回每个事件的 Previous KV,用于判断对象是从"匹配选择器"变为"不匹配"(应发送 DELETED),还是相反(应发送 ADDED)。
-
ProgressNotify:请求 etcd 定期发送空事件(仅包含 Header.Revision),即 Bookmark 事件。
-
双 goroutine 架构:
startWatching:从 etcd 接收原始事件,解析后发到incomingEventChanprocessEvent:从incomingEventChan读取,做 transform(解码+过滤),发到resultChan- 主 goroutine 监听
errChan、watchClosedCh、ctx.Done()
-
transform 过滤逻辑:
go
func (wc *watchChan) transform(e *event) (res *watch.Event) {
curObj, oldObj, err := wc.prepareObjs(e)
switch {
case e.isProgressNotify:
// Bookmark 事件
return &watch.Event{Type: watch.Bookmark, Object: object_with_rv}
case e.isDeleted:
if !wc.filter(oldObj) { return nil } // 旧对象不匹配则跳过
return &watch.Event{Type: watch.Deleted, Object: oldObj}
case e.isCreated:
if !wc.filter(curObj) { return nil } // 新对象不匹配则跳过
return &watch.Event{Type: watch.Added, Object: curObj}
default: // Modified
curObjPasses := wc.filter(curObj)
oldObjPasses := wc.filter(oldObj)
// 四种组合:
// curPass && oldPass → Modified
// curPass && !oldPass → Added (开始匹配)
// !curPass && oldPass → Deleted (不再匹配)
// !curPass && !oldPass → nil (跳过)
}
}
这个过滤逻辑非常精妙:对于一个 Update 事件,如果旧对象匹配选择器但新对象不匹配,应该产生 DELETED 事件(而非 MODIFIED);反之则产生 ADDED 事件。这确保了 Watch 的语义与 List 一致。
3.4.2 阶段二:Cacher 事件缓存与分发
#mermaid-svg-el991akUDI7BTJsv{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-el991akUDI7BTJsv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-el991akUDI7BTJsv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-el991akUDI7BTJsv .error-icon{fill:#552222;}#mermaid-svg-el991akUDI7BTJsv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-el991akUDI7BTJsv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-el991akUDI7BTJsv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-el991akUDI7BTJsv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-el991akUDI7BTJsv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-el991akUDI7BTJsv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-el991akUDI7BTJsv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-el991akUDI7BTJsv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-el991akUDI7BTJsv .marker.cross{stroke:#333333;}#mermaid-svg-el991akUDI7BTJsv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-el991akUDI7BTJsv p{margin:0;}#mermaid-svg-el991akUDI7BTJsv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-el991akUDI7BTJsv .cluster-label text{fill:#333;}#mermaid-svg-el991akUDI7BTJsv .cluster-label span{color:#333;}#mermaid-svg-el991akUDI7BTJsv .cluster-label span p{background-color:transparent;}#mermaid-svg-el991akUDI7BTJsv .label text,#mermaid-svg-el991akUDI7BTJsv span{fill:#333;color:#333;}#mermaid-svg-el991akUDI7BTJsv .node rect,#mermaid-svg-el991akUDI7BTJsv .node circle,#mermaid-svg-el991akUDI7BTJsv .node ellipse,#mermaid-svg-el991akUDI7BTJsv .node polygon,#mermaid-svg-el991akUDI7BTJsv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-el991akUDI7BTJsv .rough-node .label text,#mermaid-svg-el991akUDI7BTJsv .node .label text,#mermaid-svg-el991akUDI7BTJsv .image-shape .label,#mermaid-svg-el991akUDI7BTJsv .icon-shape .label{text-anchor:middle;}#mermaid-svg-el991akUDI7BTJsv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-el991akUDI7BTJsv .rough-node .label,#mermaid-svg-el991akUDI7BTJsv .node .label,#mermaid-svg-el991akUDI7BTJsv .image-shape .label,#mermaid-svg-el991akUDI7BTJsv .icon-shape .label{text-align:center;}#mermaid-svg-el991akUDI7BTJsv .node.clickable{cursor:pointer;}#mermaid-svg-el991akUDI7BTJsv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-el991akUDI7BTJsv .arrowheadPath{fill:#333333;}#mermaid-svg-el991akUDI7BTJsv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-el991akUDI7BTJsv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-el991akUDI7BTJsv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-el991akUDI7BTJsv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-el991akUDI7BTJsv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-el991akUDI7BTJsv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-el991akUDI7BTJsv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-el991akUDI7BTJsv .cluster text{fill:#333;}#mermaid-svg-el991akUDI7BTJsv .cluster span{color:#333;}#mermaid-svg-el991akUDI7BTJsv 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-el991akUDI7BTJsv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-el991akUDI7BTJsv rect.text{fill:none;stroke-width:0;}#mermaid-svg-el991akUDI7BTJsv .icon-shape,#mermaid-svg-el991akUDI7BTJsv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-el991akUDI7BTJsv .icon-shape p,#mermaid-svg-el991akUDI7BTJsv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-el991akUDI7BTJsv .icon-shape .label rect,#mermaid-svg-el991akUDI7BTJsv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-el991akUDI7BTJsv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-el991akUDI7BTJsv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-el991akUDI7BTJsv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Cacher
watchCache
Reflector
对象列表
变更事件
eventHandler
incoming channel 缓冲100
Bookmark
非 Bookmark
失败
ListAndWatch
watchCache.Replace
watchCache.Add/Update/Delete
环形缓冲区 cache
processEvent: 更新缓冲区 + 更新 store
Cacher.processEvent
dispatchEvents goroutine
事件类型
startDispatchingBookmarkEvents
从时间桶弹出过期 Watcher
startDispatching
从 indexedWatchers 选择相关 Watcher
nonblockingAdd 尝试非阻塞发送
blockedWatchers: 带超时阻塞发送
Cacher 的事件流转:
-
Reflector.ListAndWatch :启动时从底层 storage 执行 List 获取全量数据,调用
watchCache.Replace替换缓存;随后 Watch 底层 storage,将变更事件调用watchCache.Add/Update/Delete。 -
watchCache.processEvent:
- 获取 key、计算 labels/fields
- 从 store 中获取 previous 对象(用于过滤判断)
- 创建
watchCacheEvent放入环形缓冲区 - 更新
resourceVersion - 调用
eventHandler(即Cacher.processEvent)
-
Cacher.processEvent:
- 简单地将事件放入
incomingchannel(缓冲 100) - 不做过滤,过滤在 dispatchEvent 中完成
- 简单地将事件放入
-
dispatchEvents goroutine:
- 从
incomingchannel 读取事件 - 跳过来自存储层的 Bookmark:存储层 Bookmark 频率可能极高(sub-second),直接传播会压垮系统
- 定时(约 1 秒,带 jitter)生成 Cacher 层自己的 Bookmark 事件
- 调用
dispatchEvent分发给所有相关 Watcher
- 从
-
dispatchEvent 详解:
go
func (c *Cacher) dispatchEvent(event *watchCacheEvent) {
c.startDispatching(event) // 选择相关 Watcher
defer c.finishDispatching() // 延迟停止慢 Watcher
if event.Type == watch.Bookmark {
// Bookmark 事件:非阻塞尝试发送给所有 Watcher
for _, watcher := range c.watchersBuffer {
watcher.nonblockingAdd(event)
}
} else {
// 普通事件:先非阻塞,失败则带超时阻塞
c.blockedWatchers = c.blockedWatchers[:0]
for _, watcher := range c.watchersBuffer {
if !watcher.nonblockingAdd(event) {
c.blockedWatchers = append(c.blockedWatchers, watcher)
}
}
// 带超时重试 blocked Watchers
if len(c.blockedWatchers) > 0 {
timeout := c.dispatchTimeoutBudget.takeAvailable()
c.timer.Reset(timeout)
for _, watcher := range c.blockedWatchers {
if !watcher.add(event, timer) {
timer = nil // 已触发
}
}
// ...
}
}
}
dispatchTimeoutBudget 是一个精巧的机制:
- 每秒补充一定额度的时间预算
- 从预算中取可用时间作为超时
- 如果 Watcher 在超时内仍无法接收事件,强制终止该 Watcher
- 未用完的超时时间归还预算
这确保了:慢 Watcher 不会无限阻塞事件分发,同时也不会因为瞬时负载导致大量 Watcher 被误杀。
3.4.3 阶段三:cacheWatcher
#mermaid-svg-mgqUNiRXFWfeqG3G{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-mgqUNiRXFWfeqG3G .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mgqUNiRXFWfeqG3G .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mgqUNiRXFWfeqG3G .error-icon{fill:#552222;}#mermaid-svg-mgqUNiRXFWfeqG3G .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mgqUNiRXFWfeqG3G .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mgqUNiRXFWfeqG3G .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mgqUNiRXFWfeqG3G .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mgqUNiRXFWfeqG3G .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mgqUNiRXFWfeqG3G .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mgqUNiRXFWfeqG3G .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mgqUNiRXFWfeqG3G .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mgqUNiRXFWfeqG3G .marker.cross{stroke:#333333;}#mermaid-svg-mgqUNiRXFWfeqG3G svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mgqUNiRXFWfeqG3G p{margin:0;}#mermaid-svg-mgqUNiRXFWfeqG3G .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mgqUNiRXFWfeqG3G .cluster-label text{fill:#333;}#mermaid-svg-mgqUNiRXFWfeqG3G .cluster-label span{color:#333;}#mermaid-svg-mgqUNiRXFWfeqG3G .cluster-label span p{background-color:transparent;}#mermaid-svg-mgqUNiRXFWfeqG3G .label text,#mermaid-svg-mgqUNiRXFWfeqG3G span{fill:#333;color:#333;}#mermaid-svg-mgqUNiRXFWfeqG3G .node rect,#mermaid-svg-mgqUNiRXFWfeqG3G .node circle,#mermaid-svg-mgqUNiRXFWfeqG3G .node ellipse,#mermaid-svg-mgqUNiRXFWfeqG3G .node polygon,#mermaid-svg-mgqUNiRXFWfeqG3G .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mgqUNiRXFWfeqG3G .rough-node .label text,#mermaid-svg-mgqUNiRXFWfeqG3G .node .label text,#mermaid-svg-mgqUNiRXFWfeqG3G .image-shape .label,#mermaid-svg-mgqUNiRXFWfeqG3G .icon-shape .label{text-anchor:middle;}#mermaid-svg-mgqUNiRXFWfeqG3G .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mgqUNiRXFWfeqG3G .rough-node .label,#mermaid-svg-mgqUNiRXFWfeqG3G .node .label,#mermaid-svg-mgqUNiRXFWfeqG3G .image-shape .label,#mermaid-svg-mgqUNiRXFWfeqG3G .icon-shape .label{text-align:center;}#mermaid-svg-mgqUNiRXFWfeqG3G .node.clickable{cursor:pointer;}#mermaid-svg-mgqUNiRXFWfeqG3G .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mgqUNiRXFWfeqG3G .arrowheadPath{fill:#333333;}#mermaid-svg-mgqUNiRXFWfeqG3G .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mgqUNiRXFWfeqG3G .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mgqUNiRXFWfeqG3G .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mgqUNiRXFWfeqG3G .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mgqUNiRXFWfeqG3G .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mgqUNiRXFWfeqG3G .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mgqUNiRXFWfeqG3G .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mgqUNiRXFWfeqG3G .cluster text{fill:#333;}#mermaid-svg-mgqUNiRXFWfeqG3G .cluster span{color:#333;}#mermaid-svg-mgqUNiRXFWfeqG3G 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-mgqUNiRXFWfeqG3G .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mgqUNiRXFWfeqG3G rect.text{fill:none;stroke-width:0;}#mermaid-svg-mgqUNiRXFWfeqG3G .icon-shape,#mermaid-svg-mgqUNiRXFWfeqG3G .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mgqUNiRXFWfeqG3G .icon-shape p,#mermaid-svg-mgqUNiRXFWfeqG3G .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mgqUNiRXFWfeqG3G .icon-shape .label rect,#mermaid-svg-mgqUNiRXFWfeqG3G .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mgqUNiRXFWfeqG3G .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mgqUNiRXFWfeqG3G .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mgqUNiRXFWfeqG3G :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} cacheWatcher
是
否
input chan *watchCacheEvent
缓冲 chanSize
process goroutine
event.ResourceVersion > watchRV?
convertToWatchEvent: 类型转换+过滤
跳过: 已发送过的事件
sendWatchCacheEvent
result chan watch.Event
缓冲 chanSize
cacheWatcher 的 process 方法:
go
func (c *cacheWatcher) process(ctx context.Context, initEvents []*watchCacheEvent, resourceVersion uint64) {
// 1. 先发送初始事件
for _, event := range initEvents {
c.sendWatchCacheEvent(event)
}
// 2. 然后持续处理后续事件
for {
select {
case event, ok := <-c.input:
if !ok { return }
if event.ResourceVersion > resourceVersion {
c.sendWatchCacheEvent(event)
}
case <-ctx.Done():
return
}
}
}
初始事件的来源:当 Watch 请求到达 Cacher 时:
watchCache.GetAllEventsSinceThreadUnsafe(watchRV)获取从 watchRV 到当前的所有缓存事件- 这些事件作为
initEvents传给cacheWatcher.process resourceVersion更新为最后一个初始事件的 RV,确保后续事件不会重复发送
convertToWatchEvent 的过滤:
go
func (c *cacheWatcher) convertToWatchEvent(event *watchCacheEvent) *watch.Event {
if event.Type == watch.Bookmark {
return &watch.Event{Type: watch.Bookmark, Object: event.Object.DeepCopyObject()}
}
curObjPasses := event.Type != watch.Deleted && c.filter(event.Key, event.ObjLabels, event.ObjFields)
oldObjPasses := event.PrevObject != nil && c.filter(event.Key, event.PrevObjLabels, event.PrevObjFields)
if !curObjPasses && !oldObjPasses { return nil } // 不相关
switch {
case curObjPasses && !oldObjPasses: return Added
case curObjPasses && oldObjPasses: return Modified
case !curObjPasses && oldObjPasses: return Deleted // 使用 PrevObject
}
}
3.5 Cacher WatchCache 事件缓冲与分发
3.5.1 watchCache 环形缓冲区
#mermaid-svg-UI9iYDKK6OBzKRHy{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-UI9iYDKK6OBzKRHy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UI9iYDKK6OBzKRHy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UI9iYDKK6OBzKRHy .error-icon{fill:#552222;}#mermaid-svg-UI9iYDKK6OBzKRHy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UI9iYDKK6OBzKRHy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UI9iYDKK6OBzKRHy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UI9iYDKK6OBzKRHy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UI9iYDKK6OBzKRHy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UI9iYDKK6OBzKRHy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UI9iYDKK6OBzKRHy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UI9iYDKK6OBzKRHy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UI9iYDKK6OBzKRHy .marker.cross{stroke:#333333;}#mermaid-svg-UI9iYDKK6OBzKRHy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UI9iYDKK6OBzKRHy p{margin:0;}#mermaid-svg-UI9iYDKK6OBzKRHy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UI9iYDKK6OBzKRHy .cluster-label text{fill:#333;}#mermaid-svg-UI9iYDKK6OBzKRHy .cluster-label span{color:#333;}#mermaid-svg-UI9iYDKK6OBzKRHy .cluster-label span p{background-color:transparent;}#mermaid-svg-UI9iYDKK6OBzKRHy .label text,#mermaid-svg-UI9iYDKK6OBzKRHy span{fill:#333;color:#333;}#mermaid-svg-UI9iYDKK6OBzKRHy .node rect,#mermaid-svg-UI9iYDKK6OBzKRHy .node circle,#mermaid-svg-UI9iYDKK6OBzKRHy .node ellipse,#mermaid-svg-UI9iYDKK6OBzKRHy .node polygon,#mermaid-svg-UI9iYDKK6OBzKRHy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UI9iYDKK6OBzKRHy .rough-node .label text,#mermaid-svg-UI9iYDKK6OBzKRHy .node .label text,#mermaid-svg-UI9iYDKK6OBzKRHy .image-shape .label,#mermaid-svg-UI9iYDKK6OBzKRHy .icon-shape .label{text-anchor:middle;}#mermaid-svg-UI9iYDKK6OBzKRHy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UI9iYDKK6OBzKRHy .rough-node .label,#mermaid-svg-UI9iYDKK6OBzKRHy .node .label,#mermaid-svg-UI9iYDKK6OBzKRHy .image-shape .label,#mermaid-svg-UI9iYDKK6OBzKRHy .icon-shape .label{text-align:center;}#mermaid-svg-UI9iYDKK6OBzKRHy .node.clickable{cursor:pointer;}#mermaid-svg-UI9iYDKK6OBzKRHy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UI9iYDKK6OBzKRHy .arrowheadPath{fill:#333333;}#mermaid-svg-UI9iYDKK6OBzKRHy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UI9iYDKK6OBzKRHy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UI9iYDKK6OBzKRHy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UI9iYDKK6OBzKRHy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UI9iYDKK6OBzKRHy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UI9iYDKK6OBzKRHy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UI9iYDKK6OBzKRHy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UI9iYDKK6OBzKRHy .cluster text{fill:#333;}#mermaid-svg-UI9iYDKK6OBzKRHy .cluster span{color:#333;}#mermaid-svg-UI9iYDKK6OBzKRHy 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-UI9iYDKK6OBzKRHy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UI9iYDKK6OBzKRHy rect.text{fill:none;stroke-width:0;}#mermaid-svg-UI9iYDKK6OBzKRHy .icon-shape,#mermaid-svg-UI9iYDKK6OBzKRHy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UI9iYDKK6OBzKRHy .icon-shape p,#mermaid-svg-UI9iYDKK6OBzKRHy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UI9iYDKK6OBzKRHy .icon-shape .label rect,#mermaid-svg-UI9iYDKK6OBzKRHy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UI9iYDKK6OBzKRHy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UI9iYDKK6OBzKRHy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UI9iYDKK6OBzKRHy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} store (Indexer)
当前最新状态
所有对象的当前版本
环形缓冲区 (capacity=100)
startIndex
cache0
RV=100
cache1
RV=101
cache2
RV=102
cache3
RV=103
cache4
...
cache99
RV=199
endIndex
watchCache 的双重存储:
- 环形缓冲区
cache:存储最近的watchCacheEvent(历史事件),用于新 Watcher 的初始回放 store(cache.Indexer) :存储所有对象的当前状态,用于 List 操作
动态扩缩容:
resizeCacheLocked根据事件的时间分布调整容量- 如果缓冲区满且所有事件都在
eventFreshDuration(75s) 内:容量翻倍(上限upperBoundCapacity= 100*1024) - 如果最近 1/4 的事件超过
eventFreshDuration:容量减半(下限lowerBoundCapacity= 100) - 这确保了:高变更率时有足够的历史缓冲,低变更率时节省内存
3.5.2 WaitUntilFresh 机制
sync.Cond watchCache List/Get 请求 sync.Cond watchCache List/Get 请求 #mermaid-svg-LsIS9FYdjt1eEK6W{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-LsIS9FYdjt1eEK6W .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LsIS9FYdjt1eEK6W .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LsIS9FYdjt1eEK6W .error-icon{fill:#552222;}#mermaid-svg-LsIS9FYdjt1eEK6W .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LsIS9FYdjt1eEK6W .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LsIS9FYdjt1eEK6W .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LsIS9FYdjt1eEK6W .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LsIS9FYdjt1eEK6W .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LsIS9FYdjt1eEK6W .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LsIS9FYdjt1eEK6W .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LsIS9FYdjt1eEK6W .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LsIS9FYdjt1eEK6W .marker.cross{stroke:#333333;}#mermaid-svg-LsIS9FYdjt1eEK6W svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LsIS9FYdjt1eEK6W p{margin:0;}#mermaid-svg-LsIS9FYdjt1eEK6W .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LsIS9FYdjt1eEK6W text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-LsIS9FYdjt1eEK6W .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-LsIS9FYdjt1eEK6W .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-LsIS9FYdjt1eEK6W .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-LsIS9FYdjt1eEK6W .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-LsIS9FYdjt1eEK6W #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-LsIS9FYdjt1eEK6W .sequenceNumber{fill:white;}#mermaid-svg-LsIS9FYdjt1eEK6W #sequencenumber{fill:#333;}#mermaid-svg-LsIS9FYdjt1eEK6W #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-LsIS9FYdjt1eEK6W .messageText{fill:#333;stroke:none;}#mermaid-svg-LsIS9FYdjt1eEK6W .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LsIS9FYdjt1eEK6W .labelText,#mermaid-svg-LsIS9FYdjt1eEK6W .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-LsIS9FYdjt1eEK6W .loopText,#mermaid-svg-LsIS9FYdjt1eEK6W .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-LsIS9FYdjt1eEK6W .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-LsIS9FYdjt1eEK6W .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-LsIS9FYdjt1eEK6W .noteText,#mermaid-svg-LsIS9FYdjt1eEK6W .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-LsIS9FYdjt1eEK6W .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LsIS9FYdjt1eEK6W .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LsIS9FYdjt1eEK6W .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LsIS9FYdjt1eEK6W .actorPopupMenu{position:absolute;}#mermaid-svg-LsIS9FYdjt1eEK6W .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-LsIS9FYdjt1eEK6W .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LsIS9FYdjt1eEK6W .actor-man circle,#mermaid-svg-LsIS9FYdjt1eEK6W line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-LsIS9FYdjt1eEK6W :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 启动 3s 超时 goroutine Reflector 更新后 Broadcast loopresourceVersion \
当 List/Get 指定了 ResourceVersion 时,Cacher 会等待 watchCache 追赶到指定版本。这通过 sync.Cond 实现:
- Reflector 每次更新
resourceVersion后调用cond.Broadcast() - 等待中的 List/Get 被唤醒后检查是否满足条件
- 3 秒超时保护,避免无限等待
3.6 Bookmark 机制
#mermaid-svg-UpqoEo1NOBuGeV68{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-UpqoEo1NOBuGeV68 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UpqoEo1NOBuGeV68 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UpqoEo1NOBuGeV68 .error-icon{fill:#552222;}#mermaid-svg-UpqoEo1NOBuGeV68 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UpqoEo1NOBuGeV68 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UpqoEo1NOBuGeV68 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UpqoEo1NOBuGeV68 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UpqoEo1NOBuGeV68 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UpqoEo1NOBuGeV68 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UpqoEo1NOBuGeV68 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UpqoEo1NOBuGeV68 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UpqoEo1NOBuGeV68 .marker.cross{stroke:#333333;}#mermaid-svg-UpqoEo1NOBuGeV68 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UpqoEo1NOBuGeV68 p{margin:0;}#mermaid-svg-UpqoEo1NOBuGeV68 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UpqoEo1NOBuGeV68 .cluster-label text{fill:#333;}#mermaid-svg-UpqoEo1NOBuGeV68 .cluster-label span{color:#333;}#mermaid-svg-UpqoEo1NOBuGeV68 .cluster-label span p{background-color:transparent;}#mermaid-svg-UpqoEo1NOBuGeV68 .label text,#mermaid-svg-UpqoEo1NOBuGeV68 span{fill:#333;color:#333;}#mermaid-svg-UpqoEo1NOBuGeV68 .node rect,#mermaid-svg-UpqoEo1NOBuGeV68 .node circle,#mermaid-svg-UpqoEo1NOBuGeV68 .node ellipse,#mermaid-svg-UpqoEo1NOBuGeV68 .node polygon,#mermaid-svg-UpqoEo1NOBuGeV68 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UpqoEo1NOBuGeV68 .rough-node .label text,#mermaid-svg-UpqoEo1NOBuGeV68 .node .label text,#mermaid-svg-UpqoEo1NOBuGeV68 .image-shape .label,#mermaid-svg-UpqoEo1NOBuGeV68 .icon-shape .label{text-anchor:middle;}#mermaid-svg-UpqoEo1NOBuGeV68 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UpqoEo1NOBuGeV68 .rough-node .label,#mermaid-svg-UpqoEo1NOBuGeV68 .node .label,#mermaid-svg-UpqoEo1NOBuGeV68 .image-shape .label,#mermaid-svg-UpqoEo1NOBuGeV68 .icon-shape .label{text-align:center;}#mermaid-svg-UpqoEo1NOBuGeV68 .node.clickable{cursor:pointer;}#mermaid-svg-UpqoEo1NOBuGeV68 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UpqoEo1NOBuGeV68 .arrowheadPath{fill:#333333;}#mermaid-svg-UpqoEo1NOBuGeV68 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UpqoEo1NOBuGeV68 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UpqoEo1NOBuGeV68 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UpqoEo1NOBuGeV68 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UpqoEo1NOBuGeV68 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UpqoEo1NOBuGeV68 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UpqoEo1NOBuGeV68 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UpqoEo1NOBuGeV68 .cluster text{fill:#333;}#mermaid-svg-UpqoEo1NOBuGeV68 .cluster span{color:#333;}#mermaid-svg-UpqoEo1NOBuGeV68 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-UpqoEo1NOBuGeV68 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UpqoEo1NOBuGeV68 rect.text{fill:none;stroke-width:0;}#mermaid-svg-UpqoEo1NOBuGeV68 .icon-shape,#mermaid-svg-UpqoEo1NOBuGeV68 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UpqoEo1NOBuGeV68 .icon-shape p,#mermaid-svg-UpqoEo1NOBuGeV68 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UpqoEo1NOBuGeV68 .icon-shape .label rect,#mermaid-svg-UpqoEo1NOBuGeV68 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UpqoEo1NOBuGeV68 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UpqoEo1NOBuGeV68 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UpqoEo1NOBuGeV68 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Bookmark 分发
Bookmark 产生
是
是
否
时间桶机制
watcherBookmarkTimeBuckets
以秒为粒度的桶
addWatcher: 计算下次 Bookmark 时间
放入对应桶
popExpiredWatchers: 弹出已过期桶中的 Watcher
etcd ProgressNotify
频率: sub-second
etcd3 watchChan
parseEvent → incomingEventChan
Cacher.processEvent
incoming channel
dispatchEvents
事件类型 == Bookmark?
**丢弃** etcd 层 Bookmark
频率过高
bookmarkTimer 每约 1s
lastProcessedResourceVersion > 0?
创建 Cacher 层 Bookmark
RV = lastProcessedRV
popExpiredWatchers 仅弹出
dispatchEvent Bookmark
startDispatchingBookmarkEvents
popExpiredWatchers
从时间桶弹出
添加到 watchersBuffer
nonblockingAdd 到每个 Watcher
Bookmark 的三层意义:
-
Watch 断点续传:Bookmark 事件携带当前 ResourceVersion,客户端断连后可以用这个 RV 重新建立 Watch,避免从头发起全量 List
-
Watch 保活:某些网络环境(如负载均衡器)会关闭空闲连接。Bookmark 事件定期发送,保持连接活跃
-
减少 List 压力:没有 Bookmark 时,Watch 断连后客户端必须重新 List,大量客户端同时 List 会对 etcd 造成冲击
Bookmark 时间计算(cacheWatcher.nextBookmarkTime):
go
func (c *cacheWatcher) nextBookmarkTime(now time.Time, bookmarkFrequency time.Duration) (time.Time, bool) {
// 规则1: 大约每 bookmarkFrequency (1min) 发一次
heartbeatTime := now.Add(bookmarkFrequency)
// 规则2: 在 Watch deadline 前 2s 发一次(确保超时前有最新 RV)
if pretimeoutTime := c.deadline.Add(-2 * time.Second); pretimeoutTime.Before(heartbeatTime) {
heartbeatTime = pretimeoutTime
}
if heartbeatTime.Before(now) {
return time.Time{}, false // 已过期,不再发送
}
return heartbeatTime, true
}
3.7 List+Watch 一致性保证(ResourceVersion)
etcd Cacher kube-apiserver kubectl/informer etcd Cacher kube-apiserver kubectl/informer #mermaid-svg-PJ9VVkm7GqUqSYpj{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-PJ9VVkm7GqUqSYpj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PJ9VVkm7GqUqSYpj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PJ9VVkm7GqUqSYpj .error-icon{fill:#552222;}#mermaid-svg-PJ9VVkm7GqUqSYpj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PJ9VVkm7GqUqSYpj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PJ9VVkm7GqUqSYpj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PJ9VVkm7GqUqSYpj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PJ9VVkm7GqUqSYpj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PJ9VVkm7GqUqSYpj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PJ9VVkm7GqUqSYpj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PJ9VVkm7GqUqSYpj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PJ9VVkm7GqUqSYpj .marker.cross{stroke:#333333;}#mermaid-svg-PJ9VVkm7GqUqSYpj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PJ9VVkm7GqUqSYpj p{margin:0;}#mermaid-svg-PJ9VVkm7GqUqSYpj .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PJ9VVkm7GqUqSYpj text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-PJ9VVkm7GqUqSYpj .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-PJ9VVkm7GqUqSYpj .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-PJ9VVkm7GqUqSYpj .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-PJ9VVkm7GqUqSYpj .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-PJ9VVkm7GqUqSYpj #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-PJ9VVkm7GqUqSYpj .sequenceNumber{fill:white;}#mermaid-svg-PJ9VVkm7GqUqSYpj #sequencenumber{fill:#333;}#mermaid-svg-PJ9VVkm7GqUqSYpj #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-PJ9VVkm7GqUqSYpj .messageText{fill:#333;stroke:none;}#mermaid-svg-PJ9VVkm7GqUqSYpj .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PJ9VVkm7GqUqSYpj .labelText,#mermaid-svg-PJ9VVkm7GqUqSYpj .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-PJ9VVkm7GqUqSYpj .loopText,#mermaid-svg-PJ9VVkm7GqUqSYpj .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-PJ9VVkm7GqUqSYpj .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-PJ9VVkm7GqUqSYpj .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-PJ9VVkm7GqUqSYpj .noteText,#mermaid-svg-PJ9VVkm7GqUqSYpj .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-PJ9VVkm7GqUqSYpj .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PJ9VVkm7GqUqSYpj .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PJ9VVkm7GqUqSYpj .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PJ9VVkm7GqUqSYpj .actorPopupMenu{position:absolute;}#mermaid-svg-PJ9VVkm7GqUqSYpj .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-PJ9VVkm7GqUqSYpj .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PJ9VVkm7GqUqSYpj .actor-man circle,#mermaid-svg-PJ9VVkm7GqUqSYpj line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-PJ9VVkm7GqUqSYpj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 标准 List+Watch 流程 获取 RV > 1000 的缓存事件作为初始事件发送 loop持续 Watch GET /api/v1/pods?resourceVersion=""List(rv="")Get(prefix) 获取最新数据数据 + Header.Revision=1000PodList {RV: "1000", items: ...}Response {RV: "1000"}WATCH /api/v1/pods?resourceVersion=1000Watch(rv=1000)watchCache.GetAllEventsSinceThreadUnsafe(1000)创建 cacheWatchergo watcher.process(initEvents, 1000)事件 (RV=1001, 1002, ...)watch.Event (RV=1001, 1002, ...)Chunked event stream
一致性保证的关键:
-
List 返回的 ResourceVersion 是 etcd 的全局 Revision,不是单个 key 的 ModRevision。全局 Revision 保证所有 key 的事件有全序关系。
-
Watch 从 ListRV+1 开始:实际上在 Cacher 层,Watch 获取 RV > ListRV 的缓存事件作为初始事件,确保不会遗漏 List 后的变更。
-
"Not Older Than" 语义 :
ResourceVersionMatch=NotOlderThan允许 apiserver 返回比请求 RV 更新的数据。这在 Cacher 层天然满足------Cacher 总是返回缓存中最新的数据,只要缓存已追赶到请求的 RV。 -
RV="0" 的特殊语义:
- List RV="0":从 etcd 读取最新数据(不等待缓存)
- Watch RV="0":先 sync 当前对象(作为 ADDED 事件),然后开始 Watch
-
RV="" 的语义:
- 等价于"取最新",不指定版本约束
- Cacher 层对此会直接转发到底层 storage(兼容旧行为)
渲染错误: Mermaid 渲染失败: Parse error on line 4: ...经过缓存] B -->|RV="0" | D[从底层 storage 获 ----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'STR'
3.8 Registry Store CRUD 操作与 Strategy 模式
3.8.1 Create 完整流程
渲染错误: Mermaid 渲染失败: Parse error on line 19: ... M -->|是| N[返回 "object is being dele... ----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'STR'
Create 的关键步骤:
-
BeforeCreate(RESTCreateStrategy):
PrepareForCreate:清除 status、deletionTimestamp 等不应在创建时设置的字段GenerateName:如果只设了 GenerateName 没设 Name,自动生成唯一名称Validate:执行业务校验规则
-
createValidation:来自 Admission 链的校验,如 ValidatingAdmissionWebhook
-
AlreadyExists 处理:如果对象已存在且 DeletionTimestamp 不为 nil,返回"对象正在删除"的错误消息,避免用户困惑
-
Hook 链:BeginCreate → BeforeCreate → Admission → Storage.Create → FinishCreate → AfterCreate → Decorator
3.8.2 Update 完整流程(含 Create-on-Update)
#mermaid-svg-8oDSUjMuVF7nFhFq{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-8oDSUjMuVF7nFhFq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8oDSUjMuVF7nFhFq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8oDSUjMuVF7nFhFq .error-icon{fill:#552222;}#mermaid-svg-8oDSUjMuVF7nFhFq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8oDSUjMuVF7nFhFq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8oDSUjMuVF7nFhFq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8oDSUjMuVF7nFhFq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8oDSUjMuVF7nFhFq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8oDSUjMuVF7nFhFq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8oDSUjMuVF7nFhFq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8oDSUjMuVF7nFhFq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8oDSUjMuVF7nFhFq .marker.cross{stroke:#333333;}#mermaid-svg-8oDSUjMuVF7nFhFq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8oDSUjMuVF7nFhFq p{margin:0;}#mermaid-svg-8oDSUjMuVF7nFhFq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8oDSUjMuVF7nFhFq .cluster-label text{fill:#333;}#mermaid-svg-8oDSUjMuVF7nFhFq .cluster-label span{color:#333;}#mermaid-svg-8oDSUjMuVF7nFhFq .cluster-label span p{background-color:transparent;}#mermaid-svg-8oDSUjMuVF7nFhFq .label text,#mermaid-svg-8oDSUjMuVF7nFhFq span{fill:#333;color:#333;}#mermaid-svg-8oDSUjMuVF7nFhFq .node rect,#mermaid-svg-8oDSUjMuVF7nFhFq .node circle,#mermaid-svg-8oDSUjMuVF7nFhFq .node ellipse,#mermaid-svg-8oDSUjMuVF7nFhFq .node polygon,#mermaid-svg-8oDSUjMuVF7nFhFq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8oDSUjMuVF7nFhFq .rough-node .label text,#mermaid-svg-8oDSUjMuVF7nFhFq .node .label text,#mermaid-svg-8oDSUjMuVF7nFhFq .image-shape .label,#mermaid-svg-8oDSUjMuVF7nFhFq .icon-shape .label{text-anchor:middle;}#mermaid-svg-8oDSUjMuVF7nFhFq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8oDSUjMuVF7nFhFq .rough-node .label,#mermaid-svg-8oDSUjMuVF7nFhFq .node .label,#mermaid-svg-8oDSUjMuVF7nFhFq .image-shape .label,#mermaid-svg-8oDSUjMuVF7nFhFq .icon-shape .label{text-align:center;}#mermaid-svg-8oDSUjMuVF7nFhFq .node.clickable{cursor:pointer;}#mermaid-svg-8oDSUjMuVF7nFhFq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8oDSUjMuVF7nFhFq .arrowheadPath{fill:#333333;}#mermaid-svg-8oDSUjMuVF7nFhFq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8oDSUjMuVF7nFhFq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8oDSUjMuVF7nFhFq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8oDSUjMuVF7nFhFq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8oDSUjMuVF7nFhFq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8oDSUjMuVF7nFhFq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8oDSUjMuVF7nFhFq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8oDSUjMuVF7nFhFq .cluster text{fill:#333;}#mermaid-svg-8oDSUjMuVF7nFhFq .cluster span{color:#333;}#mermaid-svg-8oDSUjMuVF7nFhFq 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-8oDSUjMuVF7nFhFq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8oDSUjMuVF7nFhFq rect.text{fill:none;stroke-width:0;}#mermaid-svg-8oDSUjMuVF7nFhFq .icon-shape,#mermaid-svg-8oDSUjMuVF7nFhFq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8oDSUjMuVF7nFhFq .icon-shape p,#mermaid-svg-8oDSUjMuVF7nFhFq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8oDSUjMuVF7nFhFq .icon-shape .label rect,#mermaid-svg-8oDSUjMuVF7nFhFq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8oDSUjMuVF7nFhFq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8oDSUjMuVF7nFhFq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8oDSUjMuVF7nFhFq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
否
否
是
是
否
是
否
Store.Update 入口
KeyFunc: 生成 key
构建 storagePreconditions
从 objInfo 提取 UID/RV
Storage.GuaranteedUpdate
CAS 循环
tryUpdate 闭包执行
existingResourceVersion == 0?
对象不存在
AllowCreateOnUpdate?
返回 NotFound
进入创建路径:
BeforeCreate → Admission
→ 返回 obj + ttl
获取 UpdatedObject
AllowUnconditionalUpdate
且新对象 RV==0?
无条件更新: 设置 RV 为当前
新 RV == 现有 RV?
返回 Conflict
'object has been modified'
继续
BeforeUpdate: 规范化 + 校验
updateValidation: Admission
ShouldDeleteDuringUpdate?
Finalizers 清空 + deletionTimestamp 已设
返回 errEmptiedFinalizers
触发 deleteWithoutFinalizers
返回 obj, ttl
CAS 成功
creating?
AfterCreate
AfterUpdate
Update 的三种模式:
- 正常更新:对象存在,新对象 RV 匹配现有 RV → BeforeUpdate → 存储
- Create-on-Update :对象不存在且
AllowCreateOnUpdate()=true→ 走 Create 流程 - Delete-on-Update:更新导致 Finalizers 清空且 DeletionTimestamp 已设 → 触发删除
乐观并发控制的体现:
- 客户端必须提供正确的 ResourceVersion
- 如果 RV 不匹配 →
Conflict错误("the object has been modified; please apply your changes to the latest version and try again") - 如果 RV == 0 且不允许无条件更新 →
Invalid错误("must be specified for an update")
3.8.3 Delete 完整流程
#mermaid-svg-uKYX19hp6A3C0gtG{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-uKYX19hp6A3C0gtG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uKYX19hp6A3C0gtG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uKYX19hp6A3C0gtG .error-icon{fill:#552222;}#mermaid-svg-uKYX19hp6A3C0gtG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uKYX19hp6A3C0gtG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uKYX19hp6A3C0gtG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uKYX19hp6A3C0gtG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uKYX19hp6A3C0gtG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uKYX19hp6A3C0gtG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uKYX19hp6A3C0gtG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uKYX19hp6A3C0gtG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uKYX19hp6A3C0gtG .marker.cross{stroke:#333333;}#mermaid-svg-uKYX19hp6A3C0gtG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uKYX19hp6A3C0gtG p{margin:0;}#mermaid-svg-uKYX19hp6A3C0gtG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uKYX19hp6A3C0gtG .cluster-label text{fill:#333;}#mermaid-svg-uKYX19hp6A3C0gtG .cluster-label span{color:#333;}#mermaid-svg-uKYX19hp6A3C0gtG .cluster-label span p{background-color:transparent;}#mermaid-svg-uKYX19hp6A3C0gtG .label text,#mermaid-svg-uKYX19hp6A3C0gtG span{fill:#333;color:#333;}#mermaid-svg-uKYX19hp6A3C0gtG .node rect,#mermaid-svg-uKYX19hp6A3C0gtG .node circle,#mermaid-svg-uKYX19hp6A3C0gtG .node ellipse,#mermaid-svg-uKYX19hp6A3C0gtG .node polygon,#mermaid-svg-uKYX19hp6A3C0gtG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uKYX19hp6A3C0gtG .rough-node .label text,#mermaid-svg-uKYX19hp6A3C0gtG .node .label text,#mermaid-svg-uKYX19hp6A3C0gtG .image-shape .label,#mermaid-svg-uKYX19hp6A3C0gtG .icon-shape .label{text-anchor:middle;}#mermaid-svg-uKYX19hp6A3C0gtG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uKYX19hp6A3C0gtG .rough-node .label,#mermaid-svg-uKYX19hp6A3C0gtG .node .label,#mermaid-svg-uKYX19hp6A3C0gtG .image-shape .label,#mermaid-svg-uKYX19hp6A3C0gtG .icon-shape .label{text-align:center;}#mermaid-svg-uKYX19hp6A3C0gtG .node.clickable{cursor:pointer;}#mermaid-svg-uKYX19hp6A3C0gtG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uKYX19hp6A3C0gtG .arrowheadPath{fill:#333333;}#mermaid-svg-uKYX19hp6A3C0gtG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uKYX19hp6A3C0gtG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uKYX19hp6A3C0gtG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uKYX19hp6A3C0gtG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uKYX19hp6A3C0gtG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uKYX19hp6A3C0gtG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uKYX19hp6A3C0gtG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uKYX19hp6A3C0gtG .cluster text{fill:#333;}#mermaid-svg-uKYX19hp6A3C0gtG .cluster span{color:#333;}#mermaid-svg-uKYX19hp6A3C0gtG 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-uKYX19hp6A3C0gtG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uKYX19hp6A3C0gtG rect.text{fill:none;stroke-width:0;}#mermaid-svg-uKYX19hp6A3C0gtG .icon-shape,#mermaid-svg-uKYX19hp6A3C0gtG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uKYX19hp6A3C0gtG .icon-shape p,#mermaid-svg-uKYX19hp6A3C0gtG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uKYX19hp6A3C0gtG .icon-shape .label rect,#mermaid-svg-uKYX19hp6A3C0gtG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uKYX19hp6A3C0gtG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uKYX19hp6A3C0gtG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uKYX19hp6A3C0gtG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
是
否
是
否
Store.Delete 入口
Storage.Get: 获取现有对象
构建 preconditions
从 DeleteOptions 提取 UID/RV
updateForGracefulDeletionAndFinalizers
GuaranteedUpdate CAS
deleteValidation 校验
BeforeDelete:
检查是否支持 GracefulDelete
计算 gracePeriodSeconds
graceful + pendingGraceful?
返回 errAlreadyDeleting
已经在删除中
需要 GC Finalizer?
添加/移除 Orphan/DeleteDependents Finalizer
有 pendingFinalizers?
markAsDeleting: 设置 deletionTimestamp
返回更新后的对象
不真正删除
graceful?
设置 gracePeriodSeconds
返回更新后的对象
等待 grace period
返回 errDeleteNow
立即删除
conditionalDelete: Storage.Delete
真正从 etcd 删除
finalizeDelete: 后处理
Delete 的复杂状态机:
-
Finalizer 机制 :如果对象有 Finalizers,删除操作只会设置
deletionTimestamp,不会真正删除。Finalizer controller 处理完后移除 Finalizer,当所有 Finalizer 被清除后,对象才会真正被删除。 -
Graceful Deletion :支持优雅删除的资源(如 Pod)会设置
deletionGracePeriodSeconds,在 grace period 到期后才真正删除。 -
GC 策略 :根据 DeleteOptions 的
PropagationPolicy决定如何处理依赖对象:Orphan:不处理依赖Background:异步删除依赖Foreground:先删除依赖再删除自身
3.9 乐观并发控制(Optimistic Locking)
#mermaid-svg-XSy7HmDG6Hc1DcQ8{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-XSy7HmDG6Hc1DcQ8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .error-icon{fill:#552222;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .marker.cross{stroke:#333333;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 p{margin:0;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .cluster-label text{fill:#333;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .cluster-label span{color:#333;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .cluster-label span p{background-color:transparent;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .label text,#mermaid-svg-XSy7HmDG6Hc1DcQ8 span{fill:#333;color:#333;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .node rect,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .node circle,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .node ellipse,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .node polygon,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .rough-node .label text,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .node .label text,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .image-shape .label,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .rough-node .label,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .node .label,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .image-shape .label,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .icon-shape .label{text-align:center;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .node.clickable{cursor:pointer;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .arrowheadPath{fill:#333333;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .cluster text{fill:#333;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .cluster span{color:#333;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 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-XSy7HmDG6Hc1DcQ8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .icon-shape,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .icon-shape p,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .icon-shape .label rect,#mermaid-svg-XSy7HmDG6Hc1DcQ8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XSy7HmDG6Hc1DcQ8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
否
是
是
否
客户端发起 Update
读取对象
获取 ResourceVersion=X
修改对象
提交更新
携带 RV=X
Storage.GuaranteedUpdate
CAS 循环
etcd ModRevision == X?
✅ 更新成功
新 ModRevision = X+1
❌ 冲突检测
从 etcd 获取最新状态
ModRevision=Y
Preconditions 还满足?
返回 Preconditions 错误
重新调用 tryUpdate
将用户的修改应用到最新对象
tryUpdate 返回错误?
返回错误给客户端
再次 CAS 尝试
乐观并发控制的完整保证链:
-
客户端层面:
- kubectl apply: 使用 client-side 的 3-way merge,冲突时重试
- controller-runtime: Informer 的 OnUpdate 回调中使用最新对象
- Server-side Apply: 使用 managedFields 追踪字段所有权,合并时考虑冲突
-
Registry 层面:
Store.Update强制要求 ResourceVersion 匹配Store.Delete通过 Preconditions 检查 UID/RV
-
Storage 层面:
GuaranteedUpdate的 CAS 循环:If(ModRevision == expected)conditionalDelete的 CAS 循环:If(ModRevision == expected)
-
etcd 层面:
- MVCC:每次写入产生新 Revision
- Txn 原子性:If-Then-Else 在 etcd server 端原子执行
- ModRevision:每个 key 的修改版本号,是 CAS 的基础
为什么 Kubernetes 选择乐观锁而非悲观锁?
- 无死锁风险:悲观锁需要锁管理器,有死锁可能
- 适合读多写少:Kubernetes 的典型工作负载是大量 Watch + 少量 Update
- 无中心化锁:etcd 本身就是分布式协调服务,用其 MVCC 作为锁机制最自然
- 自动重试:CAS 循环自动将用户修改应用到最新版本,对调用者透明
四、Mermaid 图表集
图1:Storage 架构总览
#mermaid-svg-cHnQu400hzIegBLj{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-cHnQu400hzIegBLj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cHnQu400hzIegBLj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cHnQu400hzIegBLj .error-icon{fill:#552222;}#mermaid-svg-cHnQu400hzIegBLj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cHnQu400hzIegBLj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cHnQu400hzIegBLj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cHnQu400hzIegBLj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cHnQu400hzIegBLj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cHnQu400hzIegBLj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cHnQu400hzIegBLj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cHnQu400hzIegBLj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cHnQu400hzIegBLj .marker.cross{stroke:#333333;}#mermaid-svg-cHnQu400hzIegBLj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cHnQu400hzIegBLj p{margin:0;}#mermaid-svg-cHnQu400hzIegBLj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cHnQu400hzIegBLj .cluster-label text{fill:#333;}#mermaid-svg-cHnQu400hzIegBLj .cluster-label span{color:#333;}#mermaid-svg-cHnQu400hzIegBLj .cluster-label span p{background-color:transparent;}#mermaid-svg-cHnQu400hzIegBLj .label text,#mermaid-svg-cHnQu400hzIegBLj span{fill:#333;color:#333;}#mermaid-svg-cHnQu400hzIegBLj .node rect,#mermaid-svg-cHnQu400hzIegBLj .node circle,#mermaid-svg-cHnQu400hzIegBLj .node ellipse,#mermaid-svg-cHnQu400hzIegBLj .node polygon,#mermaid-svg-cHnQu400hzIegBLj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cHnQu400hzIegBLj .rough-node .label text,#mermaid-svg-cHnQu400hzIegBLj .node .label text,#mermaid-svg-cHnQu400hzIegBLj .image-shape .label,#mermaid-svg-cHnQu400hzIegBLj .icon-shape .label{text-anchor:middle;}#mermaid-svg-cHnQu400hzIegBLj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cHnQu400hzIegBLj .rough-node .label,#mermaid-svg-cHnQu400hzIegBLj .node .label,#mermaid-svg-cHnQu400hzIegBLj .image-shape .label,#mermaid-svg-cHnQu400hzIegBLj .icon-shape .label{text-align:center;}#mermaid-svg-cHnQu400hzIegBLj .node.clickable{cursor:pointer;}#mermaid-svg-cHnQu400hzIegBLj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cHnQu400hzIegBLj .arrowheadPath{fill:#333333;}#mermaid-svg-cHnQu400hzIegBLj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cHnQu400hzIegBLj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cHnQu400hzIegBLj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cHnQu400hzIegBLj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cHnQu400hzIegBLj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cHnQu400hzIegBLj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cHnQu400hzIegBLj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cHnQu400hzIegBLj .cluster text{fill:#333;}#mermaid-svg-cHnQu400hzIegBLj .cluster span{color:#333;}#mermaid-svg-cHnQu400hzIegBLj 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-cHnQu400hzIegBLj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cHnQu400hzIegBLj rect.text{fill:none;stroke-width:0;}#mermaid-svg-cHnQu400hzIegBLj .icon-shape,#mermaid-svg-cHnQu400hzIegBLj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cHnQu400hzIegBLj .icon-shape p,#mermaid-svg-cHnQu400hzIegBLj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cHnQu400hzIegBLj .icon-shape .label rect,#mermaid-svg-cHnQu400hzIegBLj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cHnQu400hzIegBLj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cHnQu400hzIegBLj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cHnQu400hzIegBLj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Watch 子系统
Storage 层
Registry 层
API 层
写操作委托
读操作缓存
初始事件
通过 Reflector
eventHandler
incoming
HTTP Handler
Store
CRUD + Strategy
RESTCreateStrategy
RESTUpdateStrategy
RESTDeleteStrategy
storage.Interface
Cacher
缓存 + Watch 分发
etcd3.store
etcd 客户端
watchCache
环形缓冲区
cacheWatcher
客户端 Watcher
etcd3 watchChan
etcd 原生 Watch
图7:Watch 完整链路图
#mermaid-svg-RgTcrC91zBvyeO8A{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-RgTcrC91zBvyeO8A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RgTcrC91zBvyeO8A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RgTcrC91zBvyeO8A .error-icon{fill:#552222;}#mermaid-svg-RgTcrC91zBvyeO8A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RgTcrC91zBvyeO8A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RgTcrC91zBvyeO8A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RgTcrC91zBvyeO8A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RgTcrC91zBvyeO8A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RgTcrC91zBvyeO8A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RgTcrC91zBvyeO8A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RgTcrC91zBvyeO8A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RgTcrC91zBvyeO8A .marker.cross{stroke:#333333;}#mermaid-svg-RgTcrC91zBvyeO8A svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RgTcrC91zBvyeO8A p{margin:0;}#mermaid-svg-RgTcrC91zBvyeO8A .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RgTcrC91zBvyeO8A .cluster-label text{fill:#333;}#mermaid-svg-RgTcrC91zBvyeO8A .cluster-label span{color:#333;}#mermaid-svg-RgTcrC91zBvyeO8A .cluster-label span p{background-color:transparent;}#mermaid-svg-RgTcrC91zBvyeO8A .label text,#mermaid-svg-RgTcrC91zBvyeO8A span{fill:#333;color:#333;}#mermaid-svg-RgTcrC91zBvyeO8A .node rect,#mermaid-svg-RgTcrC91zBvyeO8A .node circle,#mermaid-svg-RgTcrC91zBvyeO8A .node ellipse,#mermaid-svg-RgTcrC91zBvyeO8A .node polygon,#mermaid-svg-RgTcrC91zBvyeO8A .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RgTcrC91zBvyeO8A .rough-node .label text,#mermaid-svg-RgTcrC91zBvyeO8A .node .label text,#mermaid-svg-RgTcrC91zBvyeO8A .image-shape .label,#mermaid-svg-RgTcrC91zBvyeO8A .icon-shape .label{text-anchor:middle;}#mermaid-svg-RgTcrC91zBvyeO8A .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RgTcrC91zBvyeO8A .rough-node .label,#mermaid-svg-RgTcrC91zBvyeO8A .node .label,#mermaid-svg-RgTcrC91zBvyeO8A .image-shape .label,#mermaid-svg-RgTcrC91zBvyeO8A .icon-shape .label{text-align:center;}#mermaid-svg-RgTcrC91zBvyeO8A .node.clickable{cursor:pointer;}#mermaid-svg-RgTcrC91zBvyeO8A .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RgTcrC91zBvyeO8A .arrowheadPath{fill:#333333;}#mermaid-svg-RgTcrC91zBvyeO8A .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RgTcrC91zBvyeO8A .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RgTcrC91zBvyeO8A .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RgTcrC91zBvyeO8A .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RgTcrC91zBvyeO8A .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RgTcrC91zBvyeO8A .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RgTcrC91zBvyeO8A .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RgTcrC91zBvyeO8A .cluster text{fill:#333;}#mermaid-svg-RgTcrC91zBvyeO8A .cluster span{color:#333;}#mermaid-svg-RgTcrC91zBvyeO8A 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-RgTcrC91zBvyeO8A .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RgTcrC91zBvyeO8A rect.text{fill:none;stroke-width:0;}#mermaid-svg-RgTcrC91zBvyeO8A .icon-shape,#mermaid-svg-RgTcrC91zBvyeO8A .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RgTcrC91zBvyeO8A .icon-shape p,#mermaid-svg-RgTcrC91zBvyeO8A .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RgTcrC91zBvyeO8A .icon-shape .label rect,#mermaid-svg-RgTcrC91zBvyeO8A .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RgTcrC91zBvyeO8A .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RgTcrC91zBvyeO8A .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RgTcrC91zBvyeO8A :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} etcd
kube-apiserver
客户端
Reflector
底层 Storage
分发
WatchCache
Cacher 层
Registry 层
HTTP Watch
注册
gRPC stream
eventHandler
watchersBuffer
ResultChan
Chunked Response
kubectl/informer
Store.Watch
Cacher.Watch
-
解析 RV
-
创建 cacheWatcher
-
获取初始事件
-
注册到 watchers
cacheWatcher.process -
发送 initEvents
-
循环: input→result
GetAllEventsSinceThreadUnsafe
获取初始事件
processEvent
环形缓冲区 + store
dispatchEvents
-
startDispatching
-
nonblockingAdd
-
blockedWatchers
etcd3.WatchList
watchChan
ListAndWatch
etcd server
图8:Cacher 架构图
#mermaid-svg-2tmu8gMUR7dTofHP{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-2tmu8gMUR7dTofHP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2tmu8gMUR7dTofHP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2tmu8gMUR7dTofHP .error-icon{fill:#552222;}#mermaid-svg-2tmu8gMUR7dTofHP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2tmu8gMUR7dTofHP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2tmu8gMUR7dTofHP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2tmu8gMUR7dTofHP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2tmu8gMUR7dTofHP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2tmu8gMUR7dTofHP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2tmu8gMUR7dTofHP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2tmu8gMUR7dTofHP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2tmu8gMUR7dTofHP .marker.cross{stroke:#333333;}#mermaid-svg-2tmu8gMUR7dTofHP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2tmu8gMUR7dTofHP p{margin:0;}#mermaid-svg-2tmu8gMUR7dTofHP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2tmu8gMUR7dTofHP .cluster-label text{fill:#333;}#mermaid-svg-2tmu8gMUR7dTofHP .cluster-label span{color:#333;}#mermaid-svg-2tmu8gMUR7dTofHP .cluster-label span p{background-color:transparent;}#mermaid-svg-2tmu8gMUR7dTofHP .label text,#mermaid-svg-2tmu8gMUR7dTofHP span{fill:#333;color:#333;}#mermaid-svg-2tmu8gMUR7dTofHP .node rect,#mermaid-svg-2tmu8gMUR7dTofHP .node circle,#mermaid-svg-2tmu8gMUR7dTofHP .node ellipse,#mermaid-svg-2tmu8gMUR7dTofHP .node polygon,#mermaid-svg-2tmu8gMUR7dTofHP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2tmu8gMUR7dTofHP .rough-node .label text,#mermaid-svg-2tmu8gMUR7dTofHP .node .label text,#mermaid-svg-2tmu8gMUR7dTofHP .image-shape .label,#mermaid-svg-2tmu8gMUR7dTofHP .icon-shape .label{text-anchor:middle;}#mermaid-svg-2tmu8gMUR7dTofHP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2tmu8gMUR7dTofHP .rough-node .label,#mermaid-svg-2tmu8gMUR7dTofHP .node .label,#mermaid-svg-2tmu8gMUR7dTofHP .image-shape .label,#mermaid-svg-2tmu8gMUR7dTofHP .icon-shape .label{text-align:center;}#mermaid-svg-2tmu8gMUR7dTofHP .node.clickable{cursor:pointer;}#mermaid-svg-2tmu8gMUR7dTofHP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2tmu8gMUR7dTofHP .arrowheadPath{fill:#333333;}#mermaid-svg-2tmu8gMUR7dTofHP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2tmu8gMUR7dTofHP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2tmu8gMUR7dTofHP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2tmu8gMUR7dTofHP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2tmu8gMUR7dTofHP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2tmu8gMUR7dTofHP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2tmu8gMUR7dTofHP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2tmu8gMUR7dTofHP .cluster text{fill:#333;}#mermaid-svg-2tmu8gMUR7dTofHP .cluster span{color:#333;}#mermaid-svg-2tmu8gMUR7dTofHP 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-2tmu8gMUR7dTofHP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2tmu8gMUR7dTofHP rect.text{fill:none;stroke-width:0;}#mermaid-svg-2tmu8gMUR7dTofHP .icon-shape,#mermaid-svg-2tmu8gMUR7dTofHP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2tmu8gMUR7dTofHP .icon-shape p,#mermaid-svg-2tmu8gMUR7dTofHP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2tmu8gMUR7dTofHP .icon-shape .label rect,#mermaid-svg-2tmu8gMUR7dTofHP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2tmu8gMUR7dTofHP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2tmu8gMUR7dTofHP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2tmu8gMUR7dTofHP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Cacher
Watcher 管理
Bookmark 分发
事件分发
数据流
失败
超时
trigger 优化
缓存
watchCache
环形缓冲区 + Indexer
Reflector
ListAndWatch
incoming chan
缓冲100
dispatchEvents goroutine
startDispatching
选择相关 Watcher
watchersBuffer
nonblockingAdd
非阻塞尝试
blockedWatchers
带超时重试
强制终止 Watcher
finishDispatching
延迟停止
bookmarkTimer
~1s interval
创建 Bookmark 事件
watcherBookmarkTimeBuckets
时间桶
indexedWatchers
allWatchers + valueWatchers
图9:WatchCache 缓冲图
#mermaid-svg-1kuj4F6vUJolFsfs{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-1kuj4F6vUJolFsfs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1kuj4F6vUJolFsfs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1kuj4F6vUJolFsfs .error-icon{fill:#552222;}#mermaid-svg-1kuj4F6vUJolFsfs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1kuj4F6vUJolFsfs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1kuj4F6vUJolFsfs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1kuj4F6vUJolFsfs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1kuj4F6vUJolFsfs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1kuj4F6vUJolFsfs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1kuj4F6vUJolFsfs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1kuj4F6vUJolFsfs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1kuj4F6vUJolFsfs .marker.cross{stroke:#333333;}#mermaid-svg-1kuj4F6vUJolFsfs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1kuj4F6vUJolFsfs p{margin:0;}#mermaid-svg-1kuj4F6vUJolFsfs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1kuj4F6vUJolFsfs .cluster-label text{fill:#333;}#mermaid-svg-1kuj4F6vUJolFsfs .cluster-label span{color:#333;}#mermaid-svg-1kuj4F6vUJolFsfs .cluster-label span p{background-color:transparent;}#mermaid-svg-1kuj4F6vUJolFsfs .label text,#mermaid-svg-1kuj4F6vUJolFsfs span{fill:#333;color:#333;}#mermaid-svg-1kuj4F6vUJolFsfs .node rect,#mermaid-svg-1kuj4F6vUJolFsfs .node circle,#mermaid-svg-1kuj4F6vUJolFsfs .node ellipse,#mermaid-svg-1kuj4F6vUJolFsfs .node polygon,#mermaid-svg-1kuj4F6vUJolFsfs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1kuj4F6vUJolFsfs .rough-node .label text,#mermaid-svg-1kuj4F6vUJolFsfs .node .label text,#mermaid-svg-1kuj4F6vUJolFsfs .image-shape .label,#mermaid-svg-1kuj4F6vUJolFsfs .icon-shape .label{text-anchor:middle;}#mermaid-svg-1kuj4F6vUJolFsfs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1kuj4F6vUJolFsfs .rough-node .label,#mermaid-svg-1kuj4F6vUJolFsfs .node .label,#mermaid-svg-1kuj4F6vUJolFsfs .image-shape .label,#mermaid-svg-1kuj4F6vUJolFsfs .icon-shape .label{text-align:center;}#mermaid-svg-1kuj4F6vUJolFsfs .node.clickable{cursor:pointer;}#mermaid-svg-1kuj4F6vUJolFsfs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1kuj4F6vUJolFsfs .arrowheadPath{fill:#333333;}#mermaid-svg-1kuj4F6vUJolFsfs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1kuj4F6vUJolFsfs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1kuj4F6vUJolFsfs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1kuj4F6vUJolFsfs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1kuj4F6vUJolFsfs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1kuj4F6vUJolFsfs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1kuj4F6vUJolFsfs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1kuj4F6vUJolFsfs .cluster text{fill:#333;}#mermaid-svg-1kuj4F6vUJolFsfs .cluster span{color:#333;}#mermaid-svg-1kuj4F6vUJolFsfs 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-1kuj4F6vUJolFsfs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1kuj4F6vUJolFsfs rect.text{fill:none;stroke-width:0;}#mermaid-svg-1kuj4F6vUJolFsfs .icon-shape,#mermaid-svg-1kuj4F6vUJolFsfs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1kuj4F6vUJolFsfs .icon-shape p,#mermaid-svg-1kuj4F6vUJolFsfs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1kuj4F6vUJolFsfs .icon-shape .label rect,#mermaid-svg-1kuj4F6vUJolFsfs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1kuj4F6vUJolFsfs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1kuj4F6vUJolFsfs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1kuj4F6vUJolFsfs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Indexer Store
podA: 当前版本
labels, fields
podB: 当前版本
labels, fields
podD: 当前版本
labels, fields
环形缓冲区
0
RV=100
ADD podA
...
capacity-1
RV=199
MOD podA
1
RV=101
MOD podB
2
RV=102
DEL podC
3
...
动态容量调整:
- 缓冲区满 + 所有事件 < 75s → 扩容 2x(上限 102400)
- 最近 1/4 事件 > 75s → 缩容 /2(下限 100)
- 新 Watcher 请求初始事件时,从
startIndex到endIndex查找 RV >= 请求 RV 的事件
图10:Bookmark 机制图
cacheWatcher-2allowBookmarks=false cacheWatcher-1allowBookmarks=true Cacher.dispatchEvents watchCache Reflector etcd cacheWatcher-2allowBookmarks=false cacheWatcher-1allowBookmarks=true Cacher.dispatchEvents watchCache Reflector etcd #mermaid-svg-4QPk0BfmlRa5NHT9{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-4QPk0BfmlRa5NHT9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4QPk0BfmlRa5NHT9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4QPk0BfmlRa5NHT9 .error-icon{fill:#552222;}#mermaid-svg-4QPk0BfmlRa5NHT9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4QPk0BfmlRa5NHT9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4QPk0BfmlRa5NHT9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4QPk0BfmlRa5NHT9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4QPk0BfmlRa5NHT9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4QPk0BfmlRa5NHT9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4QPk0BfmlRa5NHT9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4QPk0BfmlRa5NHT9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4QPk0BfmlRa5NHT9 .marker.cross{stroke:#333333;}#mermaid-svg-4QPk0BfmlRa5NHT9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4QPk0BfmlRa5NHT9 p{margin:0;}#mermaid-svg-4QPk0BfmlRa5NHT9 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4QPk0BfmlRa5NHT9 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-4QPk0BfmlRa5NHT9 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4QPk0BfmlRa5NHT9 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-4QPk0BfmlRa5NHT9 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-4QPk0BfmlRa5NHT9 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-4QPk0BfmlRa5NHT9 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-4QPk0BfmlRa5NHT9 .sequenceNumber{fill:white;}#mermaid-svg-4QPk0BfmlRa5NHT9 #sequencenumber{fill:#333;}#mermaid-svg-4QPk0BfmlRa5NHT9 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-4QPk0BfmlRa5NHT9 .messageText{fill:#333;stroke:none;}#mermaid-svg-4QPk0BfmlRa5NHT9 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4QPk0BfmlRa5NHT9 .labelText,#mermaid-svg-4QPk0BfmlRa5NHT9 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-4QPk0BfmlRa5NHT9 .loopText,#mermaid-svg-4QPk0BfmlRa5NHT9 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-4QPk0BfmlRa5NHT9 .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-4QPk0BfmlRa5NHT9 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-4QPk0BfmlRa5NHT9 .noteText,#mermaid-svg-4QPk0BfmlRa5NHT9 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-4QPk0BfmlRa5NHT9 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4QPk0BfmlRa5NHT9 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4QPk0BfmlRa5NHT9 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4QPk0BfmlRa5NHT9 .actorPopupMenu{position:absolute;}#mermaid-svg-4QPk0BfmlRa5NHT9 .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-4QPk0BfmlRa5NHT9 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4QPk0BfmlRa5NHT9 .actor-man circle,#mermaid-svg-4QPk0BfmlRa5NHT9 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-4QPk0BfmlRa5NHT9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} bookmarkTimer 触发 (~1s) 不接收 Bookmark未在 bookmarkWatchers 中 ProgressNotify (sub-second)UpdateResourceVersionprocessEvent (Bookmark)**丢弃** etcd Bookmark创建 Cacher BookmarkRV = lastProcessedRVstartDispatchingBookmarkEventspopExpiredWatchers从时间桶取出nonblockingAdd (Bookmark)convertToWatchEvent返回 Bookmark 事件sendWatchCacheEvent写入 resultChan
图11:ResourceVersion 一致性图
#mermaid-svg-Iassxr0jO7TGuRTI{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-Iassxr0jO7TGuRTI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Iassxr0jO7TGuRTI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Iassxr0jO7TGuRTI .error-icon{fill:#552222;}#mermaid-svg-Iassxr0jO7TGuRTI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Iassxr0jO7TGuRTI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Iassxr0jO7TGuRTI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Iassxr0jO7TGuRTI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Iassxr0jO7TGuRTI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Iassxr0jO7TGuRTI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Iassxr0jO7TGuRTI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Iassxr0jO7TGuRTI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Iassxr0jO7TGuRTI .marker.cross{stroke:#333333;}#mermaid-svg-Iassxr0jO7TGuRTI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Iassxr0jO7TGuRTI p{margin:0;}#mermaid-svg-Iassxr0jO7TGuRTI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Iassxr0jO7TGuRTI .cluster-label text{fill:#333;}#mermaid-svg-Iassxr0jO7TGuRTI .cluster-label span{color:#333;}#mermaid-svg-Iassxr0jO7TGuRTI .cluster-label span p{background-color:transparent;}#mermaid-svg-Iassxr0jO7TGuRTI .label text,#mermaid-svg-Iassxr0jO7TGuRTI span{fill:#333;color:#333;}#mermaid-svg-Iassxr0jO7TGuRTI .node rect,#mermaid-svg-Iassxr0jO7TGuRTI .node circle,#mermaid-svg-Iassxr0jO7TGuRTI .node ellipse,#mermaid-svg-Iassxr0jO7TGuRTI .node polygon,#mermaid-svg-Iassxr0jO7TGuRTI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Iassxr0jO7TGuRTI .rough-node .label text,#mermaid-svg-Iassxr0jO7TGuRTI .node .label text,#mermaid-svg-Iassxr0jO7TGuRTI .image-shape .label,#mermaid-svg-Iassxr0jO7TGuRTI .icon-shape .label{text-anchor:middle;}#mermaid-svg-Iassxr0jO7TGuRTI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Iassxr0jO7TGuRTI .rough-node .label,#mermaid-svg-Iassxr0jO7TGuRTI .node .label,#mermaid-svg-Iassxr0jO7TGuRTI .image-shape .label,#mermaid-svg-Iassxr0jO7TGuRTI .icon-shape .label{text-align:center;}#mermaid-svg-Iassxr0jO7TGuRTI .node.clickable{cursor:pointer;}#mermaid-svg-Iassxr0jO7TGuRTI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Iassxr0jO7TGuRTI .arrowheadPath{fill:#333333;}#mermaid-svg-Iassxr0jO7TGuRTI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Iassxr0jO7TGuRTI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Iassxr0jO7TGuRTI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Iassxr0jO7TGuRTI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Iassxr0jO7TGuRTI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Iassxr0jO7TGuRTI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Iassxr0jO7TGuRTI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Iassxr0jO7TGuRTI .cluster text{fill:#333;}#mermaid-svg-Iassxr0jO7TGuRTI .cluster span{color:#333;}#mermaid-svg-Iassxr0jO7TGuRTI 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-Iassxr0jO7TGuRTI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Iassxr0jO7TGuRTI rect.text{fill:none;stroke-width:0;}#mermaid-svg-Iassxr0jO7TGuRTI .icon-shape,#mermaid-svg-Iassxr0jO7TGuRTI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Iassxr0jO7TGuRTI .icon-shape p,#mermaid-svg-Iassxr0jO7TGuRTI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Iassxr0jO7TGuRTI .icon-shape .label rect,#mermaid-svg-Iassxr0jO7TGuRTI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Iassxr0jO7TGuRTI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Iassxr0jO7TGuRTI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Iassxr0jO7TGuRTI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Watch 语义
List 语义
etcd 全局 Revision
Revision=1000
Pod A created
Revision=1001
Service B created
Revision=1002
Pod A updated
Revision=1003
ConfigMap C created
RV='' → 取最新 (Revision=1003)
RV='1000' + NotOlderThan → ≥ Revision 1000
RV='1000' + Exact → 必须 Revision 1000
RV='1000' → Watch 从 Revision 1001 开始
收到 1001, 1002, 1003, ...
RV='0' → 先 sync 当前对象
然后 Watch 当前 Revision+1
RV='' → 等同于 RV='0'
图12:Registry Store 类图
#mermaid-svg-M6kl9kMmzAi5uSIT{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-M6kl9kMmzAi5uSIT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-M6kl9kMmzAi5uSIT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-M6kl9kMmzAi5uSIT .error-icon{fill:#552222;}#mermaid-svg-M6kl9kMmzAi5uSIT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-M6kl9kMmzAi5uSIT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-M6kl9kMmzAi5uSIT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-M6kl9kMmzAi5uSIT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-M6kl9kMmzAi5uSIT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-M6kl9kMmzAi5uSIT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-M6kl9kMmzAi5uSIT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-M6kl9kMmzAi5uSIT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-M6kl9kMmzAi5uSIT .marker.cross{stroke:#333333;}#mermaid-svg-M6kl9kMmzAi5uSIT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-M6kl9kMmzAi5uSIT p{margin:0;}#mermaid-svg-M6kl9kMmzAi5uSIT g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-M6kl9kMmzAi5uSIT g.classGroup text .title{font-weight:bolder;}#mermaid-svg-M6kl9kMmzAi5uSIT .cluster-label text{fill:#333;}#mermaid-svg-M6kl9kMmzAi5uSIT .cluster-label span{color:#333;}#mermaid-svg-M6kl9kMmzAi5uSIT .cluster-label span p{background-color:transparent;}#mermaid-svg-M6kl9kMmzAi5uSIT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-M6kl9kMmzAi5uSIT .cluster text{fill:#333;}#mermaid-svg-M6kl9kMmzAi5uSIT .cluster span{color:#333;}#mermaid-svg-M6kl9kMmzAi5uSIT .nodeLabel,#mermaid-svg-M6kl9kMmzAi5uSIT .edgeLabel{color:#131300;}#mermaid-svg-M6kl9kMmzAi5uSIT .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-M6kl9kMmzAi5uSIT .label text{fill:#131300;}#mermaid-svg-M6kl9kMmzAi5uSIT .labelBkg{background:#ECECFF;}#mermaid-svg-M6kl9kMmzAi5uSIT .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-M6kl9kMmzAi5uSIT .classTitle{font-weight:bolder;}#mermaid-svg-M6kl9kMmzAi5uSIT .node rect,#mermaid-svg-M6kl9kMmzAi5uSIT .node circle,#mermaid-svg-M6kl9kMmzAi5uSIT .node ellipse,#mermaid-svg-M6kl9kMmzAi5uSIT .node polygon,#mermaid-svg-M6kl9kMmzAi5uSIT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-M6kl9kMmzAi5uSIT .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-M6kl9kMmzAi5uSIT g.clickable{cursor:pointer;}#mermaid-svg-M6kl9kMmzAi5uSIT g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-M6kl9kMmzAi5uSIT g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-M6kl9kMmzAi5uSIT .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-M6kl9kMmzAi5uSIT .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-M6kl9kMmzAi5uSIT .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-M6kl9kMmzAi5uSIT .dashed-line{stroke-dasharray:3;}#mermaid-svg-M6kl9kMmzAi5uSIT .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-M6kl9kMmzAi5uSIT #compositionStart,#mermaid-svg-M6kl9kMmzAi5uSIT .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M6kl9kMmzAi5uSIT #compositionEnd,#mermaid-svg-M6kl9kMmzAi5uSIT .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M6kl9kMmzAi5uSIT #dependencyStart,#mermaid-svg-M6kl9kMmzAi5uSIT .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M6kl9kMmzAi5uSIT #dependencyStart,#mermaid-svg-M6kl9kMmzAi5uSIT .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M6kl9kMmzAi5uSIT #extensionStart,#mermaid-svg-M6kl9kMmzAi5uSIT .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M6kl9kMmzAi5uSIT #extensionEnd,#mermaid-svg-M6kl9kMmzAi5uSIT .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M6kl9kMmzAi5uSIT #aggregationStart,#mermaid-svg-M6kl9kMmzAi5uSIT .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M6kl9kMmzAi5uSIT #aggregationEnd,#mermaid-svg-M6kl9kMmzAi5uSIT .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M6kl9kMmzAi5uSIT #lollipopStart,#mermaid-svg-M6kl9kMmzAi5uSIT .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M6kl9kMmzAi5uSIT #lollipopEnd,#mermaid-svg-M6kl9kMmzAi5uSIT .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-M6kl9kMmzAi5uSIT .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-M6kl9kMmzAi5uSIT .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-M6kl9kMmzAi5uSIT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-M6kl9kMmzAi5uSIT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-M6kl9kMmzAi5uSIT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 持有
使用
使用
使用
Store
+EnableGarbageCollection bool
+CreateStrategy RESTCreateStrategy
+UpdateStrategy RESTUpdateStrategy
+DeleteStrategy RESTDeleteStrategy
+Storage DryRunnableStorage
+NewFunc func() : runtime.Object
+NewListFunc func() : runtime.Object
+KeyRootFunc func(ctx) : string
+KeyFunc func(ctx, name)(string, error)
+ObjectNameFunc func(obj)(string, error)
+TTLFunc func(obj, existing, update)(uint64, error)
+PredicateFunc func(label, field) : SelectionPredicate
+Create(ctx, obj, createValidation, options)(runtime.Object, error)
+Update(ctx, name, objInfo, ...)(runtime.Object, bool, error)
+Get(ctx, name, options)(runtime.Object, error)
+List(ctx, options)(runtime.Object, error)
+Delete(ctx, name, deleteValidation, options)(runtime.Object, bool, error)
+Watch(ctx, options)(watch.Interface, error)
-calculateTTL(obj, existing, update)(uint64, error)
-updateForGracefulDeletionAndFinalizers(...)
-deleteWithoutFinalizers(...)
-finalizeDelete(...)
DryRunnableStorage
Interface storage.Interface
Codec runtime.Codec
RESTCreateStrategy
RESTUpdateStrategy
RESTDeleteStrategy
图13:RESTStrategy 接口图
#mermaid-svg-PIAfV15BOgenyuTE{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-PIAfV15BOgenyuTE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PIAfV15BOgenyuTE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PIAfV15BOgenyuTE .error-icon{fill:#552222;}#mermaid-svg-PIAfV15BOgenyuTE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PIAfV15BOgenyuTE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PIAfV15BOgenyuTE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PIAfV15BOgenyuTE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PIAfV15BOgenyuTE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PIAfV15BOgenyuTE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PIAfV15BOgenyuTE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PIAfV15BOgenyuTE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PIAfV15BOgenyuTE .marker.cross{stroke:#333333;}#mermaid-svg-PIAfV15BOgenyuTE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PIAfV15BOgenyuTE p{margin:0;}#mermaid-svg-PIAfV15BOgenyuTE g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-PIAfV15BOgenyuTE g.classGroup text .title{font-weight:bolder;}#mermaid-svg-PIAfV15BOgenyuTE .cluster-label text{fill:#333;}#mermaid-svg-PIAfV15BOgenyuTE .cluster-label span{color:#333;}#mermaid-svg-PIAfV15BOgenyuTE .cluster-label span p{background-color:transparent;}#mermaid-svg-PIAfV15BOgenyuTE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PIAfV15BOgenyuTE .cluster text{fill:#333;}#mermaid-svg-PIAfV15BOgenyuTE .cluster span{color:#333;}#mermaid-svg-PIAfV15BOgenyuTE .nodeLabel,#mermaid-svg-PIAfV15BOgenyuTE .edgeLabel{color:#131300;}#mermaid-svg-PIAfV15BOgenyuTE .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-PIAfV15BOgenyuTE .label text{fill:#131300;}#mermaid-svg-PIAfV15BOgenyuTE .labelBkg{background:#ECECFF;}#mermaid-svg-PIAfV15BOgenyuTE .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-PIAfV15BOgenyuTE .classTitle{font-weight:bolder;}#mermaid-svg-PIAfV15BOgenyuTE .node rect,#mermaid-svg-PIAfV15BOgenyuTE .node circle,#mermaid-svg-PIAfV15BOgenyuTE .node ellipse,#mermaid-svg-PIAfV15BOgenyuTE .node polygon,#mermaid-svg-PIAfV15BOgenyuTE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PIAfV15BOgenyuTE .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-PIAfV15BOgenyuTE g.clickable{cursor:pointer;}#mermaid-svg-PIAfV15BOgenyuTE g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-PIAfV15BOgenyuTE g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-PIAfV15BOgenyuTE .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-PIAfV15BOgenyuTE .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-PIAfV15BOgenyuTE .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-PIAfV15BOgenyuTE .dashed-line{stroke-dasharray:3;}#mermaid-svg-PIAfV15BOgenyuTE .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-PIAfV15BOgenyuTE #compositionStart,#mermaid-svg-PIAfV15BOgenyuTE .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-PIAfV15BOgenyuTE #compositionEnd,#mermaid-svg-PIAfV15BOgenyuTE .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-PIAfV15BOgenyuTE #dependencyStart,#mermaid-svg-PIAfV15BOgenyuTE .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-PIAfV15BOgenyuTE #dependencyStart,#mermaid-svg-PIAfV15BOgenyuTE .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-PIAfV15BOgenyuTE #extensionStart,#mermaid-svg-PIAfV15BOgenyuTE .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-PIAfV15BOgenyuTE #extensionEnd,#mermaid-svg-PIAfV15BOgenyuTE .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-PIAfV15BOgenyuTE #aggregationStart,#mermaid-svg-PIAfV15BOgenyuTE .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-PIAfV15BOgenyuTE #aggregationEnd,#mermaid-svg-PIAfV15BOgenyuTE .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-PIAfV15BOgenyuTE #lollipopStart,#mermaid-svg-PIAfV15BOgenyuTE .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-PIAfV15BOgenyuTE #lollipopEnd,#mermaid-svg-PIAfV15BOgenyuTE .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-PIAfV15BOgenyuTE .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-PIAfV15BOgenyuTE .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PIAfV15BOgenyuTE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PIAfV15BOgenyuTE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PIAfV15BOgenyuTE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} <<interface>>
RESTCreateStrategy
+NamespaceScoped() : bool
+PrepareForCreate(ctx, obj)
+Validate(ctx, obj) : field.ErrorList
+Canonicalize(obj)
+GenerateName(base string) : string
<<interface>>
RESTUpdateStrategy
+NamespaceScoped() : bool
+AllowCreateOnUpdate() : bool
+PrepareForUpdate(ctx, obj, old)
+ValidateUpdate(ctx, obj, old) : field.ErrorList
+Canonicalize(obj)
+AllowUnconditionalUpdate() : bool
<<interface>>
RESTDeleteStrategy
+ObjectTyper methods
<<interface>>
GarbageCollectionDeleteStrategy
+DefaultGarbageCollectionPolicy(ctx) : GarbageCollectionPolicy
<<interface>>
RESTGracefulDeleteStrategy
+CheckGracefulDelete(ctx, obj, options) : bool
<<interface>>
RESTCreateUpdateStrategy
+RESTCreateStrategy
+RESTUpdateStrategy
BeforeCreate
+BeforeCreate(strategy, ctx, obj) : error
BeforeUpdate
+BeforeUpdate(strategy, ctx, obj, old) : error
BeforeDelete
+$BeforeDelete(strategy, ctx, obj, options)(graceful, pending, error)
图14:乐观并发控制图
etcd apiserver Controller 2 Controller 1 etcd apiserver Controller 2 Controller 1 #mermaid-svg-ewsqSb5GPaOBYN7u{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-ewsqSb5GPaOBYN7u .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ewsqSb5GPaOBYN7u .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ewsqSb5GPaOBYN7u .error-icon{fill:#552222;}#mermaid-svg-ewsqSb5GPaOBYN7u .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ewsqSb5GPaOBYN7u .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ewsqSb5GPaOBYN7u .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ewsqSb5GPaOBYN7u .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ewsqSb5GPaOBYN7u .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ewsqSb5GPaOBYN7u .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ewsqSb5GPaOBYN7u .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ewsqSb5GPaOBYN7u .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ewsqSb5GPaOBYN7u .marker.cross{stroke:#333333;}#mermaid-svg-ewsqSb5GPaOBYN7u svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ewsqSb5GPaOBYN7u p{margin:0;}#mermaid-svg-ewsqSb5GPaOBYN7u .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ewsqSb5GPaOBYN7u text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ewsqSb5GPaOBYN7u .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ewsqSb5GPaOBYN7u .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ewsqSb5GPaOBYN7u .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ewsqSb5GPaOBYN7u .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ewsqSb5GPaOBYN7u #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ewsqSb5GPaOBYN7u .sequenceNumber{fill:white;}#mermaid-svg-ewsqSb5GPaOBYN7u #sequencenumber{fill:#333;}#mermaid-svg-ewsqSb5GPaOBYN7u #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ewsqSb5GPaOBYN7u .messageText{fill:#333;stroke:none;}#mermaid-svg-ewsqSb5GPaOBYN7u .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ewsqSb5GPaOBYN7u .labelText,#mermaid-svg-ewsqSb5GPaOBYN7u .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ewsqSb5GPaOBYN7u .loopText,#mermaid-svg-ewsqSb5GPaOBYN7u .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ewsqSb5GPaOBYN7u .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-ewsqSb5GPaOBYN7u .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ewsqSb5GPaOBYN7u .noteText,#mermaid-svg-ewsqSb5GPaOBYN7u .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ewsqSb5GPaOBYN7u .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ewsqSb5GPaOBYN7u .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ewsqSb5GPaOBYN7u .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ewsqSb5GPaOBYN7u .actorPopupMenu{position:absolute;}#mermaid-svg-ewsqSb5GPaOBYN7u .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-ewsqSb5GPaOBYN7u .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ewsqSb5GPaOBYN7u .actor-man circle,#mermaid-svg-ewsqSb5GPaOBYN7u line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ewsqSb5GPaOBYN7u :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 对象 Pod X 当前 ModRevision=5 两个控制器的修改都成功应用通过 CAS 循环自动解决冲突 GET pods/X → RV=5GET pods/X → RV=5修改 labels修改 annotationsPUT pods/X (RV=5)Txn: If ModRevision==5 Then PutSucceeded! ModRevision=6✅ 成功 RV=6PUT pods/X (RV=5)Txn: If ModRevision==5 Then PutFailed! ModRevision=6≠5Else: Get → 返回最新数据重试 CAS 循环:获取最新对象 (RV=6)调用 tryUpdate 重新应用修改Txn: If ModRevision==6 Then PutSucceeded! ModRevision=7✅ 成功 RV=7
图15:Storage 接口层次图
#mermaid-svg-zkAvRXNX7M0jd6YN{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-zkAvRXNX7M0jd6YN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zkAvRXNX7M0jd6YN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zkAvRXNX7M0jd6YN .error-icon{fill:#552222;}#mermaid-svg-zkAvRXNX7M0jd6YN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zkAvRXNX7M0jd6YN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zkAvRXNX7M0jd6YN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zkAvRXNX7M0jd6YN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zkAvRXNX7M0jd6YN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zkAvRXNX7M0jd6YN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zkAvRXNX7M0jd6YN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zkAvRXNX7M0jd6YN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zkAvRXNX7M0jd6YN .marker.cross{stroke:#333333;}#mermaid-svg-zkAvRXNX7M0jd6YN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zkAvRXNX7M0jd6YN p{margin:0;}#mermaid-svg-zkAvRXNX7M0jd6YN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zkAvRXNX7M0jd6YN .cluster-label text{fill:#333;}#mermaid-svg-zkAvRXNX7M0jd6YN .cluster-label span{color:#333;}#mermaid-svg-zkAvRXNX7M0jd6YN .cluster-label span p{background-color:transparent;}#mermaid-svg-zkAvRXNX7M0jd6YN .label text,#mermaid-svg-zkAvRXNX7M0jd6YN span{fill:#333;color:#333;}#mermaid-svg-zkAvRXNX7M0jd6YN .node rect,#mermaid-svg-zkAvRXNX7M0jd6YN .node circle,#mermaid-svg-zkAvRXNX7M0jd6YN .node ellipse,#mermaid-svg-zkAvRXNX7M0jd6YN .node polygon,#mermaid-svg-zkAvRXNX7M0jd6YN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zkAvRXNX7M0jd6YN .rough-node .label text,#mermaid-svg-zkAvRXNX7M0jd6YN .node .label text,#mermaid-svg-zkAvRXNX7M0jd6YN .image-shape .label,#mermaid-svg-zkAvRXNX7M0jd6YN .icon-shape .label{text-anchor:middle;}#mermaid-svg-zkAvRXNX7M0jd6YN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zkAvRXNX7M0jd6YN .rough-node .label,#mermaid-svg-zkAvRXNX7M0jd6YN .node .label,#mermaid-svg-zkAvRXNX7M0jd6YN .image-shape .label,#mermaid-svg-zkAvRXNX7M0jd6YN .icon-shape .label{text-align:center;}#mermaid-svg-zkAvRXNX7M0jd6YN .node.clickable{cursor:pointer;}#mermaid-svg-zkAvRXNX7M0jd6YN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zkAvRXNX7M0jd6YN .arrowheadPath{fill:#333333;}#mermaid-svg-zkAvRXNX7M0jd6YN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zkAvRXNX7M0jd6YN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zkAvRXNX7M0jd6YN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zkAvRXNX7M0jd6YN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zkAvRXNX7M0jd6YN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zkAvRXNX7M0jd6YN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zkAvRXNX7M0jd6YN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zkAvRXNX7M0jd6YN .cluster text{fill:#333;}#mermaid-svg-zkAvRXNX7M0jd6YN .cluster span{color:#333;}#mermaid-svg-zkAvRXNX7M0jd6YN 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-zkAvRXNX7M0jd6YN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zkAvRXNX7M0jd6YN rect.text{fill:none;stroke-width:0;}#mermaid-svg-zkAvRXNX7M0jd6YN .icon-shape,#mermaid-svg-zkAvRXNX7M0jd6YN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zkAvRXNX7M0jd6YN .icon-shape p,#mermaid-svg-zkAvRXNX7M0jd6YN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zkAvRXNX7M0jd6YN .icon-shape .label rect,#mermaid-svg-zkAvRXNX7M0jd6YN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zkAvRXNX7M0jd6YN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zkAvRXNX7M0jd6YN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zkAvRXNX7M0jd6YN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} etcd server
etcd3 (实现)
Cacher (装饰器)
Storage.Interface
Registry 层
API 请求入口
委托写/未缓存读
HTTP Handler
Store
Strategy + CRUD
Create 路径
Update 路径
Delete 路径
Get 路径
List 路径
Watch 路径
Interface
统一抽象
Cacher
缓存 + Watch 分发
watchCache
环形缓冲区
cacheWatcher
内存 Watcher
Reflector
ListAndWatch
dispatchEvents
事件分发
Bookmark 管理
时间桶
etcd3.store
etcd 客户端
Get/Put/Delete
KV 操作
Txn 事务
CAS 操作
etcd3.watcher
Watch gRPC
LeaseManager
TTL 管理
Transformer
加密转换
etcd cluster
MVCC + Raft