【client-go v0.36.1】LeaderElection 深度分析(上篇)— 模块定位、结构、LeaderElector 核心逻辑

LeaderElection 深度分析(上篇)--- 模块定位、结构、LeaderElector 核心逻辑

基于 client-go v0.36.1 tools/leaderelection/ 源码的超深度、逐行、专业级分析

源文件:leaderelection.go (561行) + leasecandidate.go (220行) + healthzadaptor.go (69行) + metrics.go (119行) + resourcelock/interface.go (179行) + resourcelock/leaselock.go (181行) + resourcelock/multilock.go (111行)


一、模块定位

1.1 业务职责与功能定位

LeaderElection 实现了 Kubernetes 中多副本控制器实例的领导者选举机制。在多副本部署场景下,同一控制器(如 kube-controller-manager)有多个实例同时运行,但只有一个实例应该执行实际调谐工作,避免重复操作。LeaderElection 通过 Lease 资源的竞争获取来决定谁是领导者。

核心职责

职责 描述
竞争选举 多个候选者通过 Lease 资源的 Get/Create/Update 竞争领导者
租约续约 领导者持续续约 Lease,证明自己仍然存活
租约过期 非领导者检测到租约过期后尝试接管
优雅释放 主动释放租约(ReleaseOnCancel),避免僵尸领导者
健康检查 HealthzAdaptor 将选举状态暴露给 /healthz 端点
协调选举 ALPHA: 基于 LeaseCandidate 的协调选举(支持优雅切换)
锁迁移 MultiLock 支持从旧锁类型迁移到新锁类型

LeaderElection 解决的核心问题:如何保证多副本控制器只有一个在执行关键路径?

1.2 在系统中的位置

#mermaid-svg-rhTTolZfPKWQKsp2{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-rhTTolZfPKWQKsp2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rhTTolZfPKWQKsp2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rhTTolZfPKWQKsp2 .error-icon{fill:#552222;}#mermaid-svg-rhTTolZfPKWQKsp2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rhTTolZfPKWQKsp2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rhTTolZfPKWQKsp2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rhTTolZfPKWQKsp2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rhTTolZfPKWQKsp2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rhTTolZfPKWQKsp2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rhTTolZfPKWQKsp2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rhTTolZfPKWQKsp2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rhTTolZfPKWQKsp2 .marker.cross{stroke:#333333;}#mermaid-svg-rhTTolZfPKWQKsp2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rhTTolZfPKWQKsp2 p{margin:0;}#mermaid-svg-rhTTolZfPKWQKsp2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rhTTolZfPKWQKsp2 .cluster-label text{fill:#333;}#mermaid-svg-rhTTolZfPKWQKsp2 .cluster-label span{color:#333;}#mermaid-svg-rhTTolZfPKWQKsp2 .cluster-label span p{background-color:transparent;}#mermaid-svg-rhTTolZfPKWQKsp2 .label text,#mermaid-svg-rhTTolZfPKWQKsp2 span{fill:#333;color:#333;}#mermaid-svg-rhTTolZfPKWQKsp2 .node rect,#mermaid-svg-rhTTolZfPKWQKsp2 .node circle,#mermaid-svg-rhTTolZfPKWQKsp2 .node ellipse,#mermaid-svg-rhTTolZfPKWQKsp2 .node polygon,#mermaid-svg-rhTTolZfPKWQKsp2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rhTTolZfPKWQKsp2 .rough-node .label text,#mermaid-svg-rhTTolZfPKWQKsp2 .node .label text,#mermaid-svg-rhTTolZfPKWQKsp2 .image-shape .label,#mermaid-svg-rhTTolZfPKWQKsp2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-rhTTolZfPKWQKsp2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rhTTolZfPKWQKsp2 .rough-node .label,#mermaid-svg-rhTTolZfPKWQKsp2 .node .label,#mermaid-svg-rhTTolZfPKWQKsp2 .image-shape .label,#mermaid-svg-rhTTolZfPKWQKsp2 .icon-shape .label{text-align:center;}#mermaid-svg-rhTTolZfPKWQKsp2 .node.clickable{cursor:pointer;}#mermaid-svg-rhTTolZfPKWQKsp2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rhTTolZfPKWQKsp2 .arrowheadPath{fill:#333333;}#mermaid-svg-rhTTolZfPKWQKsp2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rhTTolZfPKWQKsp2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rhTTolZfPKWQKsp2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rhTTolZfPKWQKsp2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rhTTolZfPKWQKsp2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rhTTolZfPKWQKsp2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rhTTolZfPKWQKsp2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rhTTolZfPKWQKsp2 .cluster text{fill:#333;}#mermaid-svg-rhTTolZfPKWQKsp2 .cluster span{color:#333;}#mermaid-svg-rhTTolZfPKWQKsp2 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-rhTTolZfPKWQKsp2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rhTTolZfPKWQKsp2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-rhTTolZfPKWQKsp2 .icon-shape,#mermaid-svg-rhTTolZfPKWQKsp2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rhTTolZfPKWQKsp2 .icon-shape p,#mermaid-svg-rhTTolZfPKWQKsp2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rhTTolZfPKWQKsp2 .icon-shape .label rect,#mermaid-svg-rhTTolZfPKWQKsp2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rhTTolZfPKWQKsp2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rhTTolZfPKWQKsp2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rhTTolZfPKWQKsp2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} API Server
LeaderElection (本次分析)
Kubernetes 控制面
Leader
Standby
Standby
kube-controller-manager #1
kube-controller-manager #2
kube-controller-manager #3
LeaderElector
LeaseLock

(resourcelock)
MultiLock

(迁移用)
HealthzAdaptor
LeaseCandidate

(ALPHA)
Metrics
Lease CRD

coordination.k8s.io
LeaseCandidate CRD

coordination.k8s.io (v1beta1)

1.3 数据流入流出

输入

  • 多个候选者通过 Lock.Get/Create/Update 竞争 Lease 资源
  • LeaseCandidate 的 Informer 事件(PingTime 触发续约)
  • 健康检查请求 (/healthz)

输出

  • OnStartedLeading(ctx) --- 成为领导者时回调
  • OnStoppedLeading() --- 失去领导权时回调
  • OnNewLeader(identity) --- 观察到新领导者时回调
  • Lease 资源的更新(续约/获取/释放)

#mermaid-svg-CreCedrb9BZ78Y1O{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-CreCedrb9BZ78Y1O .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CreCedrb9BZ78Y1O .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CreCedrb9BZ78Y1O .error-icon{fill:#552222;}#mermaid-svg-CreCedrb9BZ78Y1O .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CreCedrb9BZ78Y1O .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CreCedrb9BZ78Y1O .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CreCedrb9BZ78Y1O .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CreCedrb9BZ78Y1O .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CreCedrb9BZ78Y1O .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CreCedrb9BZ78Y1O .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CreCedrb9BZ78Y1O .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CreCedrb9BZ78Y1O .marker.cross{stroke:#333333;}#mermaid-svg-CreCedrb9BZ78Y1O svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CreCedrb9BZ78Y1O p{margin:0;}#mermaid-svg-CreCedrb9BZ78Y1O .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CreCedrb9BZ78Y1O .cluster-label text{fill:#333;}#mermaid-svg-CreCedrb9BZ78Y1O .cluster-label span{color:#333;}#mermaid-svg-CreCedrb9BZ78Y1O .cluster-label span p{background-color:transparent;}#mermaid-svg-CreCedrb9BZ78Y1O .label text,#mermaid-svg-CreCedrb9BZ78Y1O span{fill:#333;color:#333;}#mermaid-svg-CreCedrb9BZ78Y1O .node rect,#mermaid-svg-CreCedrb9BZ78Y1O .node circle,#mermaid-svg-CreCedrb9BZ78Y1O .node ellipse,#mermaid-svg-CreCedrb9BZ78Y1O .node polygon,#mermaid-svg-CreCedrb9BZ78Y1O .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CreCedrb9BZ78Y1O .rough-node .label text,#mermaid-svg-CreCedrb9BZ78Y1O .node .label text,#mermaid-svg-CreCedrb9BZ78Y1O .image-shape .label,#mermaid-svg-CreCedrb9BZ78Y1O .icon-shape .label{text-anchor:middle;}#mermaid-svg-CreCedrb9BZ78Y1O .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CreCedrb9BZ78Y1O .rough-node .label,#mermaid-svg-CreCedrb9BZ78Y1O .node .label,#mermaid-svg-CreCedrb9BZ78Y1O .image-shape .label,#mermaid-svg-CreCedrb9BZ78Y1O .icon-shape .label{text-align:center;}#mermaid-svg-CreCedrb9BZ78Y1O .node.clickable{cursor:pointer;}#mermaid-svg-CreCedrb9BZ78Y1O .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CreCedrb9BZ78Y1O .arrowheadPath{fill:#333333;}#mermaid-svg-CreCedrb9BZ78Y1O .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CreCedrb9BZ78Y1O .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CreCedrb9BZ78Y1O .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CreCedrb9BZ78Y1O .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CreCedrb9BZ78Y1O .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CreCedrb9BZ78Y1O .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CreCedrb9BZ78Y1O .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CreCedrb9BZ78Y1O .cluster text{fill:#333;}#mermaid-svg-CreCedrb9BZ78Y1O .cluster span{color:#333;}#mermaid-svg-CreCedrb9BZ78Y1O 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-CreCedrb9BZ78Y1O .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CreCedrb9BZ78Y1O rect.text{fill:none;stroke-width:0;}#mermaid-svg-CreCedrb9BZ78Y1O .icon-shape,#mermaid-svg-CreCedrb9BZ78Y1O .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CreCedrb9BZ78Y1O .icon-shape p,#mermaid-svg-CreCedrb9BZ78Y1O .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CreCedrb9BZ78Y1O .icon-shape .label rect,#mermaid-svg-CreCedrb9BZ78Y1O .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CreCedrb9BZ78Y1O .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CreCedrb9BZ78Y1O .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CreCedrb9BZ78Y1O :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输出
LeaderElection
输入
Lock.Get()
Lock.Create()
Lock.Update()
LeaseCandidate PingTime
HTTP /healthz
acquire()
renew()
Check()
Callbacks
OnStartedLeading(ctx)
OnStoppedLeading()
OnNewLeader(identity)
Lease 资源更新

1.4 选举时间参数关系

#mermaid-svg-ErdOp6DonHGlSp07{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-ErdOp6DonHGlSp07 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ErdOp6DonHGlSp07 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ErdOp6DonHGlSp07 .error-icon{fill:#552222;}#mermaid-svg-ErdOp6DonHGlSp07 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ErdOp6DonHGlSp07 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ErdOp6DonHGlSp07 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ErdOp6DonHGlSp07 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ErdOp6DonHGlSp07 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ErdOp6DonHGlSp07 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ErdOp6DonHGlSp07 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ErdOp6DonHGlSp07 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ErdOp6DonHGlSp07 .marker.cross{stroke:#333333;}#mermaid-svg-ErdOp6DonHGlSp07 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ErdOp6DonHGlSp07 p{margin:0;}#mermaid-svg-ErdOp6DonHGlSp07 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ErdOp6DonHGlSp07 .cluster-label text{fill:#333;}#mermaid-svg-ErdOp6DonHGlSp07 .cluster-label span{color:#333;}#mermaid-svg-ErdOp6DonHGlSp07 .cluster-label span p{background-color:transparent;}#mermaid-svg-ErdOp6DonHGlSp07 .label text,#mermaid-svg-ErdOp6DonHGlSp07 span{fill:#333;color:#333;}#mermaid-svg-ErdOp6DonHGlSp07 .node rect,#mermaid-svg-ErdOp6DonHGlSp07 .node circle,#mermaid-svg-ErdOp6DonHGlSp07 .node ellipse,#mermaid-svg-ErdOp6DonHGlSp07 .node polygon,#mermaid-svg-ErdOp6DonHGlSp07 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ErdOp6DonHGlSp07 .rough-node .label text,#mermaid-svg-ErdOp6DonHGlSp07 .node .label text,#mermaid-svg-ErdOp6DonHGlSp07 .image-shape .label,#mermaid-svg-ErdOp6DonHGlSp07 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ErdOp6DonHGlSp07 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ErdOp6DonHGlSp07 .rough-node .label,#mermaid-svg-ErdOp6DonHGlSp07 .node .label,#mermaid-svg-ErdOp6DonHGlSp07 .image-shape .label,#mermaid-svg-ErdOp6DonHGlSp07 .icon-shape .label{text-align:center;}#mermaid-svg-ErdOp6DonHGlSp07 .node.clickable{cursor:pointer;}#mermaid-svg-ErdOp6DonHGlSp07 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ErdOp6DonHGlSp07 .arrowheadPath{fill:#333333;}#mermaid-svg-ErdOp6DonHGlSp07 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ErdOp6DonHGlSp07 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ErdOp6DonHGlSp07 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ErdOp6DonHGlSp07 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ErdOp6DonHGlSp07 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ErdOp6DonHGlSp07 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ErdOp6DonHGlSp07 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ErdOp6DonHGlSp07 .cluster text{fill:#333;}#mermaid-svg-ErdOp6DonHGlSp07 .cluster span{color:#333;}#mermaid-svg-ErdOp6DonHGlSp07 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-ErdOp6DonHGlSp07 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ErdOp6DonHGlSp07 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ErdOp6DonHGlSp07 .icon-shape,#mermaid-svg-ErdOp6DonHGlSp07 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ErdOp6DonHGlSp07 .icon-shape p,#mermaid-svg-ErdOp6DonHGlSp07 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ErdOp6DonHGlSp07 .icon-shape .label rect,#mermaid-svg-ErdOp6DonHGlSp07 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ErdOp6DonHGlSp07 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ErdOp6DonHGlSp07 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ErdOp6DonHGlSp07 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 语义
约束关系
时间参数 (默认值)
LeaseDuration = 15s

租约有效期
RenewDeadline = 10s

续约截止时间
RetryPeriod = 2s

重试间隔
LeaseDuration > RenewDeadline

(15 > 10) ✓
RenewDeadline > RetryPeriod × JitterFactor

(10 > 2×1.2=2.4) ✓
非领导者等待 LeaseDuration

后才能尝试接管
领导者在 RenewDeadline 内

必须成功续约一次
每隔 RetryPeriod 尝试一次

(带 1.2x 抖动)


二、模块整体结构

2.1 类结构、接口定义、依赖注入关系

#mermaid-svg-CV4EZc1oyNh5PcIF{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-CV4EZc1oyNh5PcIF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CV4EZc1oyNh5PcIF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CV4EZc1oyNh5PcIF .error-icon{fill:#552222;}#mermaid-svg-CV4EZc1oyNh5PcIF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CV4EZc1oyNh5PcIF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CV4EZc1oyNh5PcIF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CV4EZc1oyNh5PcIF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CV4EZc1oyNh5PcIF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CV4EZc1oyNh5PcIF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CV4EZc1oyNh5PcIF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CV4EZc1oyNh5PcIF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CV4EZc1oyNh5PcIF .marker.cross{stroke:#333333;}#mermaid-svg-CV4EZc1oyNh5PcIF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CV4EZc1oyNh5PcIF p{margin:0;}#mermaid-svg-CV4EZc1oyNh5PcIF g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-CV4EZc1oyNh5PcIF g.classGroup text .title{font-weight:bolder;}#mermaid-svg-CV4EZc1oyNh5PcIF .cluster-label text{fill:#333;}#mermaid-svg-CV4EZc1oyNh5PcIF .cluster-label span{color:#333;}#mermaid-svg-CV4EZc1oyNh5PcIF .cluster-label span p{background-color:transparent;}#mermaid-svg-CV4EZc1oyNh5PcIF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CV4EZc1oyNh5PcIF .cluster text{fill:#333;}#mermaid-svg-CV4EZc1oyNh5PcIF .cluster span{color:#333;}#mermaid-svg-CV4EZc1oyNh5PcIF .nodeLabel,#mermaid-svg-CV4EZc1oyNh5PcIF .edgeLabel{color:#131300;}#mermaid-svg-CV4EZc1oyNh5PcIF .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-CV4EZc1oyNh5PcIF .label text{fill:#131300;}#mermaid-svg-CV4EZc1oyNh5PcIF .labelBkg{background:#ECECFF;}#mermaid-svg-CV4EZc1oyNh5PcIF .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-CV4EZc1oyNh5PcIF .classTitle{font-weight:bolder;}#mermaid-svg-CV4EZc1oyNh5PcIF .node rect,#mermaid-svg-CV4EZc1oyNh5PcIF .node circle,#mermaid-svg-CV4EZc1oyNh5PcIF .node ellipse,#mermaid-svg-CV4EZc1oyNh5PcIF .node polygon,#mermaid-svg-CV4EZc1oyNh5PcIF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CV4EZc1oyNh5PcIF .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-CV4EZc1oyNh5PcIF g.clickable{cursor:pointer;}#mermaid-svg-CV4EZc1oyNh5PcIF g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-CV4EZc1oyNh5PcIF g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-CV4EZc1oyNh5PcIF .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-CV4EZc1oyNh5PcIF .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-CV4EZc1oyNh5PcIF .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-CV4EZc1oyNh5PcIF .dashed-line{stroke-dasharray:3;}#mermaid-svg-CV4EZc1oyNh5PcIF .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-CV4EZc1oyNh5PcIF #compositionStart,#mermaid-svg-CV4EZc1oyNh5PcIF .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-CV4EZc1oyNh5PcIF #compositionEnd,#mermaid-svg-CV4EZc1oyNh5PcIF .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-CV4EZc1oyNh5PcIF #dependencyStart,#mermaid-svg-CV4EZc1oyNh5PcIF .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-CV4EZc1oyNh5PcIF #dependencyStart,#mermaid-svg-CV4EZc1oyNh5PcIF .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-CV4EZc1oyNh5PcIF #extensionStart,#mermaid-svg-CV4EZc1oyNh5PcIF .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-CV4EZc1oyNh5PcIF #extensionEnd,#mermaid-svg-CV4EZc1oyNh5PcIF .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-CV4EZc1oyNh5PcIF #aggregationStart,#mermaid-svg-CV4EZc1oyNh5PcIF .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-CV4EZc1oyNh5PcIF #aggregationEnd,#mermaid-svg-CV4EZc1oyNh5PcIF .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-CV4EZc1oyNh5PcIF #lollipopStart,#mermaid-svg-CV4EZc1oyNh5PcIF .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-CV4EZc1oyNh5PcIF #lollipopEnd,#mermaid-svg-CV4EZc1oyNh5PcIF .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-CV4EZc1oyNh5PcIF .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-CV4EZc1oyNh5PcIF .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CV4EZc1oyNh5PcIF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CV4EZc1oyNh5PcIF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CV4EZc1oyNh5PcIF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} uses
contains
depends on
optional
tracks
references
implements
implements
wraps two
contains
optional (Coordinated)
LeaderElector
-config LeaderElectionConfig
-observedRecord LeaderElectionRecord
-observedRawRecord \[\]byte
-observedTime time.Time
-reportedLeader string
-clock clock.Clock
-observedRecordLock sync.RWMutex
-metrics leaderMetricsAdapter
+Run(ctx)
+acquire(ctx) : bool
+renew(ctx)
+tryAcquireOrRenew(ctx) : bool
+tryCoordinatedRenew(ctx) : bool
+release(logger) : bool
+IsLeader() : bool
+GetLeader() : string
+Check(maxTolerableExpiredLease) : error
-maybeReportTransition()
-isLeaseValid(now) : bool
-setObservedRecord(record)
-getObservedRecord() : LeaderElectionRecord
LeaderElectionConfig
+Lock rl.Interface
+LeaseDuration time.Duration
+RenewDeadline time.Duration
+RetryPeriod time.Duration
+Callbacks LeaderCallbacks
+WatchDog *HealthzAdaptor
+ReleaseOnCancel bool
+Name string
+Coordinated bool
LeaderCallbacks
+OnStartedLeading func(ctx)
+OnStoppedLeading func()
+OnNewLeader func(identity)
LeaderElectionRecord
+HolderIdentity string
+LeaseDurationSeconds int
+AcquireTime metav1.Time
+RenewTime metav1.Time
+LeaderTransitions int
+Strategy v1.CoordinatedLeaseStrategy
+PreferredHolder string
HealthzAdaptor
-pointerLock sync.Mutex
-le *LeaderElector
-timeout time.Duration
+Name() : string
+Check(req) : error
+SetLeaderElection(le)
LeaseCandidate
-leaseClient LeaseCandidateInterface
-leaseCandidateInformer SharedIndexInformer
-leaseCandidateLister LeaseCandidateLister
-informerFactory SharedInformerFactory
-hasSynced InformerSynced
-queue TypedRateLimitingInterface
-name string
-namespace string
-leaseName string
-clock clock.Clock
-binaryVersion string
-emulationVersion string
-strategy v1.CoordinatedLeaseStrategy
+Run(ctx)
-runWorker(ctx)
-processNextWorkItem(ctx) : bool
-ensureLease(ctx) : error
-enqueueLease()
-newLeaseCandidate() : *v1beta1.LeaseCandidate
<<interface>>
rl_Interface
+Get(ctx)(*LeaderElectionRecord, \[\]byte, error)
+Create(ctx, ler) : error
+Update(ctx, ler) : error
+RecordEvent(string)
+Identity() : string
+Describe() : string
LeaseLock
+LeaseMeta metav1.ObjectMeta
+Client LeasesGetter
+LockConfig ResourceLockConfig
-lease *coordinationv1.Lease
+Labels map<string>string
+Get(ctx)(*LeaderElectionRecord, \[\]byte, error)
+Create(ctx, ler) : error
+Update(ctx, ler) : error
+RecordEvent(string)
+Describe() : string
+Identity() : string
MultiLock
+Primary Interface
+Secondary Interface
+Get(ctx)(*LeaderElectionRecord, \[\]byte, error)
+Create(ctx, ler) : error
+Update(ctx, ler) : error
+RecordEvent(string)
+Describe() : string
+Identity() : string
ResourceLockConfig
+Identity string
+EventRecorder EventRecorder

2.2 核心方法清单与作用

LeaderElector 方法

方法 作用 阻塞
Run(ctx) 主循环:acquire + renew ✅ 直到 ctx 取消或失去租约
acquire(ctx) 竞争获取领导权 ✅ 直到成功或 ctx 取消
renew(ctx) 持续续约 ✅ 直到续约失败或 ctx 取消
tryAcquireOrRenew(ctx) 单次尝试获取/续约
tryCoordinatedRenew(ctx) 协调模式下的续约
release(logger) 主动释放租约
IsLeader() 当前是否是领导者
GetLeader() 获取当前领导者 ID
Check(timeout) 健康检查
maybeReportTransition() 报告领导权转移

LeaseLock 方法

方法 作用
Get(ctx) 获取 Lease → 转换为 LeaderElectionRecord
Create(ctx, ler) 创建 Lease
Update(ctx, ler) 更新 Lease
RecordEvent(s) 记录事件
Identity() 返回锁持有者 ID
Describe() 返回 ns/name 描述

LeaseCandidate 方法

方法 作用
Run(ctx) 启动候选者控制器
ensureLease(ctx) 创建/续约 LeaseCandidate
enqueueLease() 将工作项入队

2.3 内部调用关系

#mermaid-svg-t83gU4CB6WjpGxFT{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-t83gU4CB6WjpGxFT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-t83gU4CB6WjpGxFT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-t83gU4CB6WjpGxFT .error-icon{fill:#552222;}#mermaid-svg-t83gU4CB6WjpGxFT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-t83gU4CB6WjpGxFT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-t83gU4CB6WjpGxFT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-t83gU4CB6WjpGxFT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-t83gU4CB6WjpGxFT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-t83gU4CB6WjpGxFT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-t83gU4CB6WjpGxFT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-t83gU4CB6WjpGxFT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-t83gU4CB6WjpGxFT .marker.cross{stroke:#333333;}#mermaid-svg-t83gU4CB6WjpGxFT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-t83gU4CB6WjpGxFT p{margin:0;}#mermaid-svg-t83gU4CB6WjpGxFT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-t83gU4CB6WjpGxFT .cluster-label text{fill:#333;}#mermaid-svg-t83gU4CB6WjpGxFT .cluster-label span{color:#333;}#mermaid-svg-t83gU4CB6WjpGxFT .cluster-label span p{background-color:transparent;}#mermaid-svg-t83gU4CB6WjpGxFT .label text,#mermaid-svg-t83gU4CB6WjpGxFT span{fill:#333;color:#333;}#mermaid-svg-t83gU4CB6WjpGxFT .node rect,#mermaid-svg-t83gU4CB6WjpGxFT .node circle,#mermaid-svg-t83gU4CB6WjpGxFT .node ellipse,#mermaid-svg-t83gU4CB6WjpGxFT .node polygon,#mermaid-svg-t83gU4CB6WjpGxFT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-t83gU4CB6WjpGxFT .rough-node .label text,#mermaid-svg-t83gU4CB6WjpGxFT .node .label text,#mermaid-svg-t83gU4CB6WjpGxFT .image-shape .label,#mermaid-svg-t83gU4CB6WjpGxFT .icon-shape .label{text-anchor:middle;}#mermaid-svg-t83gU4CB6WjpGxFT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-t83gU4CB6WjpGxFT .rough-node .label,#mermaid-svg-t83gU4CB6WjpGxFT .node .label,#mermaid-svg-t83gU4CB6WjpGxFT .image-shape .label,#mermaid-svg-t83gU4CB6WjpGxFT .icon-shape .label{text-align:center;}#mermaid-svg-t83gU4CB6WjpGxFT .node.clickable{cursor:pointer;}#mermaid-svg-t83gU4CB6WjpGxFT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-t83gU4CB6WjpGxFT .arrowheadPath{fill:#333333;}#mermaid-svg-t83gU4CB6WjpGxFT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-t83gU4CB6WjpGxFT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-t83gU4CB6WjpGxFT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t83gU4CB6WjpGxFT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-t83gU4CB6WjpGxFT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t83gU4CB6WjpGxFT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-t83gU4CB6WjpGxFT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-t83gU4CB6WjpGxFT .cluster text{fill:#333;}#mermaid-svg-t83gU4CB6WjpGxFT .cluster span{color:#333;}#mermaid-svg-t83gU4CB6WjpGxFT 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-t83gU4CB6WjpGxFT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-t83gU4CB6WjpGxFT rect.text{fill:none;stroke-width:0;}#mermaid-svg-t83gU4CB6WjpGxFT .icon-shape,#mermaid-svg-t83gU4CB6WjpGxFT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t83gU4CB6WjpGxFT .icon-shape p,#mermaid-svg-t83gU4CB6WjpGxFT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-t83gU4CB6WjpGxFT .icon-shape .label rect,#mermaid-svg-t83gU4CB6WjpGxFT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t83gU4CB6WjpGxFT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-t83gU4CB6WjpGxFT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-t83gU4CB6WjpGxFT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HealthzAdaptor
LeaseLock
Lock (Interface)
Callbacks
LeaderElector
Run(ctx)
acquire(ctx)
renew(ctx)
tryAcquireOrRenew(ctx)
tryCoordinatedRenew(ctx)
release(logger)
maybeReportTransition()
isLeaseValid(now)
setObservedRecord()
getObservedRecord()
Check(timeout)
OnStartedLeading(ctx)
OnStoppedLeading()
OnNewLeader(identity)
Lock.Get()
Lock.Create()
Lock.Update()
LeaseLock.Get()
LeaseLock.Create()
LeaseLock.Update()
LeaseSpecToLeaderElectionRecord()
LeaderElectionRecordToLeaseSpec()
HealthzAdaptor.Check()
SetLeaderElection()


三、核心业务逻辑深度解析 --- LeaderElector

3.1 NewLeaderElector --- 构造函数

go 复制代码
func NewLeaderElector(lec LeaderElectionConfig) (*LeaderElector, error) {
    // ─── 参数校验 ───

    // 约束1: LeaseDuration 必须大于 RenewDeadline
    // 语义:租约有效期必须长于续约截止时间
    // 否则:领导者还没来得及续约,租约就过期了
    if lec.LeaseDuration <= lec.RenewDeadline {
        return nil, fmt.Errorf("leaseDuration must be greater than renewDeadline")
    }

    // 约束2: RenewDeadline 必须大于 RetryPeriod × JitterFactor
    // JitterFactor = 1.2,即 2s × 1.2 = 2.4s
    // 语义:续约截止时间内至少能完成一次重试(考虑抖动)
    // 否则:可能还没重试一次就超时了
    if lec.RenewDeadline <= time.Duration(JitterFactor*float64(lec.RetryPeriod)) {
        return nil, fmt.Errorf("renewDeadline must be greater than retryPeriod*JitterFactor")
    }

    // 约束3-5: 三个时间参数都必须大于 0
    if lec.LeaseDuration < 1 {
        return nil, fmt.Errorf("leaseDuration must be greater than zero")
    }
    if lec.RenewDeadline < 1 {
        return nil, fmt.Errorf("renewDeadline must be greater than zero")
    }
    if lec.RetryPeriod < 1 {
        return nil, fmt.Errorf("retryPeriod must be greater than zero")
    }

    // 约束6-7: 必须的回调函数
    if lec.Callbacks.OnStartedLeading == nil {
        return nil, fmt.Errorf("OnStartedLeading callback must not be nil")
    }
    if lec.Callbacks.OnStoppedLeading == nil {
        return nil, fmt.Errorf("OnStoppedLeading callback must not be nil")
    }
    // OnNewLeader 可以为 nil(可选回调)

    // 约束8: Lock 不能为空
    if lec.Lock == nil {
        return nil, fmt.Errorf("Lock must not be nil.")
    }

    // 约束9: Lock Identity 不能为空
    id := lec.Lock.Identity()
    if id == "" {
        return nil, fmt.Errorf("Lock identity is empty")
    }

    // ─── 构造 LeaderElector ───
    le := LeaderElector{
        config:  lec,
        clock:   clock.RealClock{},                    // 使用真实时钟
        metrics: globalMetricsFactory.newLeaderMetrics(), // 全局指标工厂
    }
    le.metrics.leaderOff(le.config.Name)              // 初始状态:不是领导者
    return &le, nil
}

参数校验决策树
#mermaid-svg-M4E9amvvRWJenOMN{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-M4E9amvvRWJenOMN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-M4E9amvvRWJenOMN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-M4E9amvvRWJenOMN .error-icon{fill:#552222;}#mermaid-svg-M4E9amvvRWJenOMN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-M4E9amvvRWJenOMN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-M4E9amvvRWJenOMN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-M4E9amvvRWJenOMN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-M4E9amvvRWJenOMN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-M4E9amvvRWJenOMN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-M4E9amvvRWJenOMN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-M4E9amvvRWJenOMN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-M4E9amvvRWJenOMN .marker.cross{stroke:#333333;}#mermaid-svg-M4E9amvvRWJenOMN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-M4E9amvvRWJenOMN p{margin:0;}#mermaid-svg-M4E9amvvRWJenOMN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-M4E9amvvRWJenOMN .cluster-label text{fill:#333;}#mermaid-svg-M4E9amvvRWJenOMN .cluster-label span{color:#333;}#mermaid-svg-M4E9amvvRWJenOMN .cluster-label span p{background-color:transparent;}#mermaid-svg-M4E9amvvRWJenOMN .label text,#mermaid-svg-M4E9amvvRWJenOMN span{fill:#333;color:#333;}#mermaid-svg-M4E9amvvRWJenOMN .node rect,#mermaid-svg-M4E9amvvRWJenOMN .node circle,#mermaid-svg-M4E9amvvRWJenOMN .node ellipse,#mermaid-svg-M4E9amvvRWJenOMN .node polygon,#mermaid-svg-M4E9amvvRWJenOMN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-M4E9amvvRWJenOMN .rough-node .label text,#mermaid-svg-M4E9amvvRWJenOMN .node .label text,#mermaid-svg-M4E9amvvRWJenOMN .image-shape .label,#mermaid-svg-M4E9amvvRWJenOMN .icon-shape .label{text-anchor:middle;}#mermaid-svg-M4E9amvvRWJenOMN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-M4E9amvvRWJenOMN .rough-node .label,#mermaid-svg-M4E9amvvRWJenOMN .node .label,#mermaid-svg-M4E9amvvRWJenOMN .image-shape .label,#mermaid-svg-M4E9amvvRWJenOMN .icon-shape .label{text-align:center;}#mermaid-svg-M4E9amvvRWJenOMN .node.clickable{cursor:pointer;}#mermaid-svg-M4E9amvvRWJenOMN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-M4E9amvvRWJenOMN .arrowheadPath{fill:#333333;}#mermaid-svg-M4E9amvvRWJenOMN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-M4E9amvvRWJenOMN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-M4E9amvvRWJenOMN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M4E9amvvRWJenOMN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-M4E9amvvRWJenOMN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M4E9amvvRWJenOMN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-M4E9amvvRWJenOMN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-M4E9amvvRWJenOMN .cluster text{fill:#333;}#mermaid-svg-M4E9amvvRWJenOMN .cluster span{color:#333;}#mermaid-svg-M4E9amvvRWJenOMN 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-M4E9amvvRWJenOMN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-M4E9amvvRWJenOMN rect.text{fill:none;stroke-width:0;}#mermaid-svg-M4E9amvvRWJenOMN .icon-shape,#mermaid-svg-M4E9amvvRWJenOMN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M4E9amvvRWJenOMN .icon-shape p,#mermaid-svg-M4E9amvvRWJenOMN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-M4E9amvvRWJenOMN .icon-shape .label rect,#mermaid-svg-M4E9amvvRWJenOMN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M4E9amvvRWJenOMN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-M4E9amvvRWJenOMN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-M4E9amvvRWJenOMN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} No
Yes
No
Yes
No
No
No
No
No
No
No
Yes
NewLeaderElector(config)
LeaseDuration > RenewDeadline?
RenewDeadline > RetryPeriod × 1.2?
LeaseDuration > 0?
RenewDeadline > 0?
RetryPeriod > 0?
OnStartedLeading ≠ nil?
OnStoppedLeading ≠ nil?
Lock ≠ nil?
Lock.Identity() ≠ ''?
return &LeaderElector, nil
error: leaseDuration must be greater than renewDeadline
error: renewDeadline must be greater than retryPeriod*JitterFactor
error: xxx must be greater than zero
error: OnStartedLeading callback must not be nil
error: OnStoppedLeading callback must not be nil
error: Lock must not be nil
error: Lock identity is empty

3.2 Run --- 主循环

go 复制代码
func (le *LeaderElector) Run(ctx context.Context) {
    // ─── 步骤1: defer 链(LIFO 执行顺序) ───

    // 最后执行:panic 恢复
    defer runtime.HandleCrashWithContext(ctx)
    // 倒数第二执行:OnStoppedLeading 回调
    // 注意:无论是否曾成为领导者,OnStoppedLeading 都会被调用
    // 这是故意的设计:即使从未成为领导者,退出时也需要清理
    defer le.config.Callbacks.OnStoppedLeading()

    // ─── 步骤2: 竞争获取领导权 ───
    if !le.acquire(ctx) {
        return  // ctx 被取消 → 退出
        // 注意:此时 OnStoppedLeading 已经通过 defer 被调度
    }

    // ─── 步骤3: 成为领导者 ───
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()

    // 异步启动用户的领导者逻辑
    // 用户的 OnStartedLeading 收到一个 ctx,
    // 当失去领导权时这个 ctx 会被 cancel
    go le.config.Callbacks.OnStartedLeading(ctx)

    // ─── 步骤4: 持续续约 ───
    le.renew(ctx)
    // renew 返回意味着失去了领导权
    // 此时 ctx 被 cancel → 用户的 OnStartedLeading 逻辑应该退出
}

Run 的完整生命周期
#mermaid-svg-Xc4wdUGuRBFs5Abe{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-Xc4wdUGuRBFs5Abe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Xc4wdUGuRBFs5Abe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Xc4wdUGuRBFs5Abe .error-icon{fill:#552222;}#mermaid-svg-Xc4wdUGuRBFs5Abe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Xc4wdUGuRBFs5Abe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Xc4wdUGuRBFs5Abe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Xc4wdUGuRBFs5Abe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Xc4wdUGuRBFs5Abe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Xc4wdUGuRBFs5Abe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Xc4wdUGuRBFs5Abe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Xc4wdUGuRBFs5Abe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Xc4wdUGuRBFs5Abe .marker.cross{stroke:#333333;}#mermaid-svg-Xc4wdUGuRBFs5Abe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Xc4wdUGuRBFs5Abe p{margin:0;}#mermaid-svg-Xc4wdUGuRBFs5Abe defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-Xc4wdUGuRBFs5Abe g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-Xc4wdUGuRBFs5Abe g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-Xc4wdUGuRBFs5Abe g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-Xc4wdUGuRBFs5Abe g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-Xc4wdUGuRBFs5Abe g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-Xc4wdUGuRBFs5Abe .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-Xc4wdUGuRBFs5Abe .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-Xc4wdUGuRBFs5Abe .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-Xc4wdUGuRBFs5Abe .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Xc4wdUGuRBFs5Abe .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-Xc4wdUGuRBFs5Abe .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-Xc4wdUGuRBFs5Abe .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-Xc4wdUGuRBFs5Abe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xc4wdUGuRBFs5Abe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Xc4wdUGuRBFs5Abe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xc4wdUGuRBFs5Abe .edgeLabel .label text{fill:#333;}#mermaid-svg-Xc4wdUGuRBFs5Abe .label div .edgeLabel{color:#333;}#mermaid-svg-Xc4wdUGuRBFs5Abe .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-Xc4wdUGuRBFs5Abe .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-Xc4wdUGuRBFs5Abe .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-Xc4wdUGuRBFs5Abe .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-Xc4wdUGuRBFs5Abe .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-Xc4wdUGuRBFs5Abe .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Xc4wdUGuRBFs5Abe .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Xc4wdUGuRBFs5Abe #statediagram-barbEnd{fill:#333333;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Xc4wdUGuRBFs5Abe .cluster-label,#mermaid-svg-Xc4wdUGuRBFs5Abe .nodeLabel{color:#131300;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-Xc4wdUGuRBFs5Abe .note-edge{stroke-dasharray:5;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-note text{fill:black;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram-note .nodeLabel{color:black;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagram .edgeLabel{color:red;}#mermaid-svg-Xc4wdUGuRBFs5Abe #dependencyStart,#mermaid-svg-Xc4wdUGuRBFs5Abe #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-Xc4wdUGuRBFs5Abe .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Xc4wdUGuRBFs5Abe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Run()
acquire() succeeds
ctx cancelled during acquire
OnStartedLeading(ctx) goroutine
renew fails
ctx cancelled during renew
ReleaseOnCancel=true
ReleaseOnCancel=false
release() done
defer OnStoppedLeading()
Acquiring
Leading
Stopped
Renewing
LostLease
Releasing
Lock (Lease) OnStartedLeading renew() acquire() LeaderElector Context Lock (Lease) OnStartedLeading renew() acquire() LeaderElector Context #mermaid-svg-NL1aJmAphjcm3vNw{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-NL1aJmAphjcm3vNw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NL1aJmAphjcm3vNw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NL1aJmAphjcm3vNw .error-icon{fill:#552222;}#mermaid-svg-NL1aJmAphjcm3vNw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NL1aJmAphjcm3vNw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NL1aJmAphjcm3vNw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NL1aJmAphjcm3vNw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NL1aJmAphjcm3vNw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NL1aJmAphjcm3vNw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NL1aJmAphjcm3vNw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NL1aJmAphjcm3vNw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NL1aJmAphjcm3vNw .marker.cross{stroke:#333333;}#mermaid-svg-NL1aJmAphjcm3vNw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NL1aJmAphjcm3vNw p{margin:0;}#mermaid-svg-NL1aJmAphjcm3vNw .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NL1aJmAphjcm3vNw text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-NL1aJmAphjcm3vNw .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-NL1aJmAphjcm3vNw .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-NL1aJmAphjcm3vNw .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-NL1aJmAphjcm3vNw .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-NL1aJmAphjcm3vNw #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-NL1aJmAphjcm3vNw .sequenceNumber{fill:white;}#mermaid-svg-NL1aJmAphjcm3vNw #sequencenumber{fill:#333;}#mermaid-svg-NL1aJmAphjcm3vNw #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-NL1aJmAphjcm3vNw .messageText{fill:#333;stroke:none;}#mermaid-svg-NL1aJmAphjcm3vNw .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NL1aJmAphjcm3vNw .labelText,#mermaid-svg-NL1aJmAphjcm3vNw .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-NL1aJmAphjcm3vNw .loopText,#mermaid-svg-NL1aJmAphjcm3vNw .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-NL1aJmAphjcm3vNw .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-NL1aJmAphjcm3vNw .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-NL1aJmAphjcm3vNw .noteText,#mermaid-svg-NL1aJmAphjcm3vNw .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-NL1aJmAphjcm3vNw .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NL1aJmAphjcm3vNw .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NL1aJmAphjcm3vNw .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NL1aJmAphjcm3vNw .actorPopupMenu{position:absolute;}#mermaid-svg-NL1aJmAphjcm3vNw .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-NL1aJmAphjcm3vNw .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NL1aJmAphjcm3vNw .actor-man circle,#mermaid-svg-NL1aJmAphjcm3vNw line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-NL1aJmAphjcm3vNw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} loop每 RetryPeriod (带抖动) 用户开始执行领导者逻辑 loop在 RenewDeadline 内重试 继续循环 ctx 被取消 → 应退出 alt续约成功续约失败 loop每 RetryPeriod Run(ctx)acquire(ctx)tryAcquireOrRenew()true/falseacquired = truego OnStartedLeading(ctx)renew(ctx)PollUntilContextTimeout(RetryPeriod, RenewDeadline)tryAcquireOrRenew()true/false失去领导权cancel()defer OnStoppedLeading()

3.3 acquire --- 竞争获取领导权

go 复制代码
func (le *LeaderElector) acquire(ctx context.Context) bool {
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()

    succeeded := false
    desc := le.config.Lock.Describe()
    logger := klog.FromContext(ctx)
    logger.Info("Attempting to acquire leader lease...", "lock", desc)

    // JitterUntilWithContext: 以 RetryPeriod 为间隔,带 JitterFactor=1.2 抖动
    // 第4个参数 true = sliding(从函数返回后开始计时)
    wait.JitterUntilWithContext(ctx, func(ctx context.Context) {
        if !le.config.Coordinated {
            // 标准模式:tryAcquireOrRenew
            succeeded = le.tryAcquireOrRenew(ctx)
        } else {
            // 协调模式:tryCoordinatedRenew
            succeeded = le.tryCoordinatedRenew(ctx)
        }
        le.maybeReportTransition()

        if !succeeded {
            logger.V(4).Info("Failed to acquire lease", "lock", desc)
            return
            // 失败 → 等待下次重试
        }

        // 成功 → 记录事件 + 更新指标 + 取消上下文退出循环
        le.config.Lock.RecordEvent("became leader")
        le.metrics.leaderOn(le.config.Name)
        logger.Info("Successfully acquired lease", "lock", desc)
        cancel()
        // cancel() → JitterUntilWithContext 检测到 ctx.Done() → 退出
    }, le.config.RetryPeriod, JitterFactor, true)

    return succeeded
    // ctx 取消 → succeeded=false → Run 退出
    // acquire 成功 → succeeded=true → Run 继续到 renew
}

acquire 流程
#mermaid-svg-Q142BvNkJZB8Gjah{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-Q142BvNkJZB8Gjah .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Q142BvNkJZB8Gjah .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Q142BvNkJZB8Gjah .error-icon{fill:#552222;}#mermaid-svg-Q142BvNkJZB8Gjah .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Q142BvNkJZB8Gjah .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Q142BvNkJZB8Gjah .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Q142BvNkJZB8Gjah .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Q142BvNkJZB8Gjah .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Q142BvNkJZB8Gjah .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Q142BvNkJZB8Gjah .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Q142BvNkJZB8Gjah .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Q142BvNkJZB8Gjah .marker.cross{stroke:#333333;}#mermaid-svg-Q142BvNkJZB8Gjah svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Q142BvNkJZB8Gjah p{margin:0;}#mermaid-svg-Q142BvNkJZB8Gjah .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Q142BvNkJZB8Gjah .cluster-label text{fill:#333;}#mermaid-svg-Q142BvNkJZB8Gjah .cluster-label span{color:#333;}#mermaid-svg-Q142BvNkJZB8Gjah .cluster-label span p{background-color:transparent;}#mermaid-svg-Q142BvNkJZB8Gjah .label text,#mermaid-svg-Q142BvNkJZB8Gjah span{fill:#333;color:#333;}#mermaid-svg-Q142BvNkJZB8Gjah .node rect,#mermaid-svg-Q142BvNkJZB8Gjah .node circle,#mermaid-svg-Q142BvNkJZB8Gjah .node ellipse,#mermaid-svg-Q142BvNkJZB8Gjah .node polygon,#mermaid-svg-Q142BvNkJZB8Gjah .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Q142BvNkJZB8Gjah .rough-node .label text,#mermaid-svg-Q142BvNkJZB8Gjah .node .label text,#mermaid-svg-Q142BvNkJZB8Gjah .image-shape .label,#mermaid-svg-Q142BvNkJZB8Gjah .icon-shape .label{text-anchor:middle;}#mermaid-svg-Q142BvNkJZB8Gjah .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Q142BvNkJZB8Gjah .rough-node .label,#mermaid-svg-Q142BvNkJZB8Gjah .node .label,#mermaid-svg-Q142BvNkJZB8Gjah .image-shape .label,#mermaid-svg-Q142BvNkJZB8Gjah .icon-shape .label{text-align:center;}#mermaid-svg-Q142BvNkJZB8Gjah .node.clickable{cursor:pointer;}#mermaid-svg-Q142BvNkJZB8Gjah .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Q142BvNkJZB8Gjah .arrowheadPath{fill:#333333;}#mermaid-svg-Q142BvNkJZB8Gjah .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Q142BvNkJZB8Gjah .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Q142BvNkJZB8Gjah .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q142BvNkJZB8Gjah .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Q142BvNkJZB8Gjah .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q142BvNkJZB8Gjah .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Q142BvNkJZB8Gjah .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Q142BvNkJZB8Gjah .cluster text{fill:#333;}#mermaid-svg-Q142BvNkJZB8Gjah .cluster span{color:#333;}#mermaid-svg-Q142BvNkJZB8Gjah 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-Q142BvNkJZB8Gjah .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Q142BvNkJZB8Gjah rect.text{fill:none;stroke-width:0;}#mermaid-svg-Q142BvNkJZB8Gjah .icon-shape,#mermaid-svg-Q142BvNkJZB8Gjah .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q142BvNkJZB8Gjah .icon-shape p,#mermaid-svg-Q142BvNkJZB8Gjah .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Q142BvNkJZB8Gjah .icon-shape .label rect,#mermaid-svg-Q142BvNkJZB8Gjah .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q142BvNkJZB8Gjah .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Q142BvNkJZB8Gjah .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Q142BvNkJZB8Gjah :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} No
Yes
No
Yes
acquire(ctx)
succeeded = false
JitterUntilWithContext

(RetryPeriod, JitterFactor=1.2)
Coordinated?
tryAcquireOrRenew(ctx)
tryCoordinatedRenew(ctx)
maybeReportTransition()
succeeded?
V(4): Failed to acquire lease
RecordEvent('became leader')
metrics.leaderOn()
cancel() → 退出循环
return succeeded

3.4 renew --- 持续续约

go 复制代码
func (le *LeaderElector) renew(ctx context.Context) {
    defer le.config.Lock.RecordEvent("stopped leading")
    // 无论何时退出 renew,都记录 "stopped leading" 事件

    ctx, cancel := context.WithCancel(ctx)
    defer cancel()
    logger := klog.FromContext(ctx)

    // UntilWithContext: 以 RetryPeriod 为间隔无限循环
    // 直到 ctx 被取消
    wait.UntilWithContext(ctx, func(ctx context.Context) {
        // 在每次 RetryPeriod 间隔内,使用 PollUntilContextTimeout 进行续约
        // PollUntilContextTimeout:
        //   - 以 RetryPeriod 为内部重试间隔
        //   - 以 RenewDeadline 为总体超时
        //   - immediate=true: 第一次立即执行
        err := wait.PollUntilContextTimeout(ctx, le.config.RetryPeriod, le.config.RenewDeadline, true,
            func(ctx context.Context) (done bool, err error) {
                // 短路:ctx 已取消 → 不执行
                if err := ctx.Err(); err != nil {
                    return false, err
                }
                if !le.config.Coordinated {
                    return le.tryAcquireOrRenew(ctx), nil
                } else {
                    return le.tryCoordinatedRenew(ctx), nil
                }
            },
        )

        le.maybeReportTransition()

        if err == nil {
            logger.V(5).Info("Successfully renewed lease", "lock", le.config.Lock.Describe())
            return
            // 续约成功 → 等待下一个 RetryPeriod 再续约
        }

        // 续约失败 → 失去领导权
        le.metrics.leaderOff(le.config.Name)
        logger.Info("Failed to renew lease", "lock", le.config.Lock.Describe(), "err", err)
        cancel()
        // cancel → UntilWithContext 退出
    }, le.config.RetryPeriod)

    // ─── 失去领导权后的处理 ───
    if le.config.ReleaseOnCancel {
        le.release(logger)
        // 主动释放租约,将 HolderIdentity 清空
        // 避免:领导者已停止但仍持有 Lease → 其他候选者需等 LeaseDuration
    }
}

renew 的双层循环结构
#mermaid-svg-CVfNwwSTVa6jcJz2{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-CVfNwwSTVa6jcJz2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CVfNwwSTVa6jcJz2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CVfNwwSTVa6jcJz2 .error-icon{fill:#552222;}#mermaid-svg-CVfNwwSTVa6jcJz2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CVfNwwSTVa6jcJz2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CVfNwwSTVa6jcJz2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CVfNwwSTVa6jcJz2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CVfNwwSTVa6jcJz2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CVfNwwSTVa6jcJz2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CVfNwwSTVa6jcJz2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CVfNwwSTVa6jcJz2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CVfNwwSTVa6jcJz2 .marker.cross{stroke:#333333;}#mermaid-svg-CVfNwwSTVa6jcJz2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CVfNwwSTVa6jcJz2 p{margin:0;}#mermaid-svg-CVfNwwSTVa6jcJz2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CVfNwwSTVa6jcJz2 .cluster-label text{fill:#333;}#mermaid-svg-CVfNwwSTVa6jcJz2 .cluster-label span{color:#333;}#mermaid-svg-CVfNwwSTVa6jcJz2 .cluster-label span p{background-color:transparent;}#mermaid-svg-CVfNwwSTVa6jcJz2 .label text,#mermaid-svg-CVfNwwSTVa6jcJz2 span{fill:#333;color:#333;}#mermaid-svg-CVfNwwSTVa6jcJz2 .node rect,#mermaid-svg-CVfNwwSTVa6jcJz2 .node circle,#mermaid-svg-CVfNwwSTVa6jcJz2 .node ellipse,#mermaid-svg-CVfNwwSTVa6jcJz2 .node polygon,#mermaid-svg-CVfNwwSTVa6jcJz2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CVfNwwSTVa6jcJz2 .rough-node .label text,#mermaid-svg-CVfNwwSTVa6jcJz2 .node .label text,#mermaid-svg-CVfNwwSTVa6jcJz2 .image-shape .label,#mermaid-svg-CVfNwwSTVa6jcJz2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-CVfNwwSTVa6jcJz2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CVfNwwSTVa6jcJz2 .rough-node .label,#mermaid-svg-CVfNwwSTVa6jcJz2 .node .label,#mermaid-svg-CVfNwwSTVa6jcJz2 .image-shape .label,#mermaid-svg-CVfNwwSTVa6jcJz2 .icon-shape .label{text-align:center;}#mermaid-svg-CVfNwwSTVa6jcJz2 .node.clickable{cursor:pointer;}#mermaid-svg-CVfNwwSTVa6jcJz2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CVfNwwSTVa6jcJz2 .arrowheadPath{fill:#333333;}#mermaid-svg-CVfNwwSTVa6jcJz2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CVfNwwSTVa6jcJz2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CVfNwwSTVa6jcJz2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CVfNwwSTVa6jcJz2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CVfNwwSTVa6jcJz2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CVfNwwSTVa6jcJz2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CVfNwwSTVa6jcJz2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CVfNwwSTVa6jcJz2 .cluster text{fill:#333;}#mermaid-svg-CVfNwwSTVa6jcJz2 .cluster span{color:#333;}#mermaid-svg-CVfNwwSTVa6jcJz2 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-CVfNwwSTVa6jcJz2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CVfNwwSTVa6jcJz2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-CVfNwwSTVa6jcJz2 .icon-shape,#mermaid-svg-CVfNwwSTVa6jcJz2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CVfNwwSTVa6jcJz2 .icon-shape p,#mermaid-svg-CVfNwwSTVa6jcJz2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CVfNwwSTVa6jcJz2 .icon-shape .label rect,#mermaid-svg-CVfNwwSTVa6jcJz2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CVfNwwSTVa6jcJz2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CVfNwwSTVa6jcJz2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CVfNwwSTVa6jcJz2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Yes
No
UntilWithContext

(RetryPeriod 间隔)
PollUntilContextTimeout

(RetryPeriod 间隔, RenewDeadline 超时)
tryAcquireOrRenew()
续约成功?
V(5): renewed

return → 等下次
metrics.leaderOff()

cancel() → 退出
ReleaseOnCancel?
release(logger)
退出
渲染错误: Mermaid 渲染失败: Parse error on line 14: ... POLL-->>LOOP: err=nil → 继续 -----------------------^ Expecting '+', '-', '()', 'ACTOR', got 'loop'

3.5 tryAcquireOrRenew --- 单次获取/续约尝试(核心算法)

go 复制代码
func (le *LeaderElector) tryAcquireOrRenew(ctx context.Context) bool {
    logger := klog.FromContext(ctx)
    now := metav1.NewTime(le.clock.Now())

    // ─── 构造本地的选举记录 ───
    leaderElectionRecord := rl.LeaderElectionRecord{
        HolderIdentity:       le.config.Lock.Identity(),     // 本候选者 ID
        LeaseDurationSeconds: int(le.config.LeaseDuration / time.Second),
        RenewTime:            now,                           // 续约时间 = 当前时间
        AcquireTime:          now,                           // 获取时间 = 当前时间(如果是新获取)
    }

    // ═══ 步骤1: Fast Path --- 乐观更新 ═══
    // 如果当前是领导者且租约有效,直接 Update 而不先 Get
    // 这是最常见的路径(领导者大部分时间都在续约)
    if le.IsLeader() && le.isLeaseValid(now.Time) {
        oldObservedRecord := le.getObservedRecord()
        leaderElectionRecord.AcquireTime = oldObservedRecord.AcquireTime
        // 保持原始获取时间不变
        leaderElectionRecord.LeaderTransitions = oldObservedRecord.LeaderTransitions
        // 保持领导权转移次数不变

        err := le.config.Lock.Update(ctx, leaderElectionRecord)
        if err == nil {
            le.setObservedRecord(&leaderElectionRecord)
            return true
            // Fast Path 成功 → 直接返回
        }
        // Fast Path 失败(可能是冲突/网络问题)→ 回退到 Slow Path
        logger.V(2).Info("Failed to update lease optimistically, falling back to slow path",
            "lock", le.config.Lock.Describe(), "err", err)
    }

    // ═══ 步骤2: Slow Path --- Get 或 Create ═══
    oldLeaderElectionRecord, oldLeaderElectionRawRecord, err := le.config.Lock.Get(ctx)
    if err != nil {
        if !errors.IsNotFound(err) {
            logger.Error(err, "Error retrieving lease lock", "lock", le.config.Lock.Describe())
            return false
            // 其他错误(网络/权限)→ 失败
        }
        // Lease 不存在 → 创建
        if err = le.config.Lock.Create(ctx, leaderElectionRecord); err != nil {
            logger.Error(err, "Error initially creating lease lock", "lock", le.config.Lock.Describe())
            return false
        }
        le.setObservedRecord(&leaderElectionRecord)
        return true
        // 创建成功 → 成为领导者
    }

    // ═══ 步骤3: 检查当前租约状态 ═══
    // 如果 raw record 发生变化 → 更新本地观测
    if !bytes.Equal(le.observedRawRecord, oldLeaderElectionRawRecord) {
        le.setObservedRecord(oldLeaderElectionRecord)
        le.observedRawRecord = oldLeaderElectionRawRecord
    }

    // 检查:是否有持有者 + 租约是否有效 + 自己不是领导者
    if len(oldLeaderElectionRecord.HolderIdentity) > 0 &&
       le.isLeaseValid(now.Time) &&
       !le.IsLeader() {
        // 有其他领导者且租约未过期 → 不能抢
        logger.V(4).Info("Lease is held by and has not yet expired",
            "lock", le.config.Lock.Describe(),
            "holder", oldLeaderElectionRecord.HolderIdentity)
        return false
    }

    // ═══ 步骤4: 尝试更新(获取或续约)═══
    if le.IsLeader() {
        // 自己是领导者 → 续约(Slow Path)
        leaderElectionRecord.AcquireTime = oldLeaderElectionRecord.AcquireTime
        // 保持获取时间
        leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions
        // 保持转移次数
        le.metrics.slowpathExercised(le.config.Name)
        // 记录走了 Slow Path(监控用)
    } else {
        // 自己不是领导者 → 获取领导权
        leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions + 1
        // 转移次数 +1
    }

    // 执行 Update
    if err = le.config.Lock.Update(ctx, leaderElectionRecord); err != nil {
        logger.Error(err, "Failed to update lease", "lock", le.config.Lock.Describe())
        return false
    }

    le.setObservedRecord(&leaderElectionRecord)
    return true
}

tryAcquireOrRenew 完整决策流程
#mermaid-svg-MDa320m5FqdUxzRZ{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-MDa320m5FqdUxzRZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MDa320m5FqdUxzRZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MDa320m5FqdUxzRZ .error-icon{fill:#552222;}#mermaid-svg-MDa320m5FqdUxzRZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MDa320m5FqdUxzRZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MDa320m5FqdUxzRZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MDa320m5FqdUxzRZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MDa320m5FqdUxzRZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MDa320m5FqdUxzRZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MDa320m5FqdUxzRZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MDa320m5FqdUxzRZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MDa320m5FqdUxzRZ .marker.cross{stroke:#333333;}#mermaid-svg-MDa320m5FqdUxzRZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MDa320m5FqdUxzRZ p{margin:0;}#mermaid-svg-MDa320m5FqdUxzRZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MDa320m5FqdUxzRZ .cluster-label text{fill:#333;}#mermaid-svg-MDa320m5FqdUxzRZ .cluster-label span{color:#333;}#mermaid-svg-MDa320m5FqdUxzRZ .cluster-label span p{background-color:transparent;}#mermaid-svg-MDa320m5FqdUxzRZ .label text,#mermaid-svg-MDa320m5FqdUxzRZ span{fill:#333;color:#333;}#mermaid-svg-MDa320m5FqdUxzRZ .node rect,#mermaid-svg-MDa320m5FqdUxzRZ .node circle,#mermaid-svg-MDa320m5FqdUxzRZ .node ellipse,#mermaid-svg-MDa320m5FqdUxzRZ .node polygon,#mermaid-svg-MDa320m5FqdUxzRZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MDa320m5FqdUxzRZ .rough-node .label text,#mermaid-svg-MDa320m5FqdUxzRZ .node .label text,#mermaid-svg-MDa320m5FqdUxzRZ .image-shape .label,#mermaid-svg-MDa320m5FqdUxzRZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-MDa320m5FqdUxzRZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MDa320m5FqdUxzRZ .rough-node .label,#mermaid-svg-MDa320m5FqdUxzRZ .node .label,#mermaid-svg-MDa320m5FqdUxzRZ .image-shape .label,#mermaid-svg-MDa320m5FqdUxzRZ .icon-shape .label{text-align:center;}#mermaid-svg-MDa320m5FqdUxzRZ .node.clickable{cursor:pointer;}#mermaid-svg-MDa320m5FqdUxzRZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MDa320m5FqdUxzRZ .arrowheadPath{fill:#333333;}#mermaid-svg-MDa320m5FqdUxzRZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MDa320m5FqdUxzRZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MDa320m5FqdUxzRZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MDa320m5FqdUxzRZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MDa320m5FqdUxzRZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MDa320m5FqdUxzRZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MDa320m5FqdUxzRZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MDa320m5FqdUxzRZ .cluster text{fill:#333;}#mermaid-svg-MDa320m5FqdUxzRZ .cluster span{color:#333;}#mermaid-svg-MDa320m5FqdUxzRZ 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-MDa320m5FqdUxzRZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MDa320m5FqdUxzRZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-MDa320m5FqdUxzRZ .icon-shape,#mermaid-svg-MDa320m5FqdUxzRZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MDa320m5FqdUxzRZ .icon-shape p,#mermaid-svg-MDa320m5FqdUxzRZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MDa320m5FqdUxzRZ .icon-shape .label rect,#mermaid-svg-MDa320m5FqdUxzRZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MDa320m5FqdUxzRZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MDa320m5FqdUxzRZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MDa320m5FqdUxzRZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
Yes
No
No
NotFound
Yes
No
其他错误
nil
变化
未变化
Yes
No
Yes
No
Yes
No
tryAcquireOrRenew(ctx)
构造 leaderElectionRecord

Holder=自己, RenewTime=now, AcquireTime=now
Fast Path: IsLeader() && isLeaseValid()?
Lock.Update(乐观)
Update 成功?
return true (Fast Path)
回退到 Slow Path
Lock.Get() → oldRecord, oldRaw
err?
Lock.Create(record)
Create 成功?
return true
return false
raw record 变化?
setObservedRecord + observedRawRecord
有持有者 && 租约有效 && 不是自己?
return false

(租约被他人持有)
IsLeader()?
续约 Slow Path:

保持 AcquireTime + Transitions

metrics.slowpathExercised
获取领导权:

Transitions + 1
Lock.Update(record)
Update 成功?
setObservedRecord
return true
return false

3.6 tryCoordinatedRenew --- 协调模式续约

go 复制代码
func (le *LeaderElector) tryCoordinatedRenew(ctx context.Context) bool {
    logger := klog.FromContext(ctx)
    now := metav1.NewTime(le.clock.Now())

    leaderElectionRecord := rl.LeaderElectionRecord{
        HolderIdentity:       le.config.Lock.Identity(),
        LeaseDurationSeconds: int(le.config.LeaseDuration / time.Second),
        RenewTime:            now,
        AcquireTime:          now,
    }

    // ─── 步骤1: 获取当前 Lease ───
    oldLeaderElectionRecord, oldLeaderElectionRawRecord, err := le.config.Lock.Get(ctx)
    if err != nil {
        if !errors.IsNotFound(err) {
            logger.Error(err, "Error retrieving lease lock")
            return false
        }
        logger.Info("Lease lock not found")
        return false
        // 注意:协调模式下 NotFound 直接返回 false,不尝试 Create
        // 因为协调选举需要通过 LeaseCandidate 协商
    }

    // ─── 步骤2: 检查租约状态 ───
    if !bytes.Equal(le.observedRawRecord, oldLeaderElectionRawRecord) {
        le.setObservedRecord(oldLeaderElectionRecord)
        le.observedRawRecord = oldLeaderElectionRawRecord
    }

    // 检查租约是否过期
    le.observedRecordLock.RLock()
    obsTime := le.observedTime
    le.observedRecordLock.RUnlock()
    hasExpired := obsTime.Add(time.Second * time.Duration(oldLeaderElectionRecord.LeaseDurationSeconds)).Before(now.Time)
    if hasExpired {
        logger.Info("Lease has expired")
        return false
    }

    // 自己不是领导者 → 不能续约
    if !le.IsLeader() {
        logger.V(6).Info("Lease is held and has not yet expired",
            "holder", oldLeaderElectionRecord.HolderIdentity)
        return false
    }

    // ─── 步骤2b: 检查是否被标记为 "任期结束" ───
    // 如果 Lease 上有 PreferredHolder,意味着协调选举已选出新的偏好领导者
    // 当前持有者应该停止续约,让新领导者接管
    if le.IsLeader() && oldLeaderElectionRecord.PreferredHolder != "" {
        logger.V(4).Info("Lease is marked as 'end of term'")
        return false
        // 不续约 → 租约自然过期 → PreferredHolder 接管
    }

    // ─── 步骤3: 更新 Lease ───
    if le.IsLeader() {
        leaderElectionRecord.AcquireTime = oldLeaderElectionRecord.AcquireTime
        leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions
        leaderElectionRecord.Strategy = oldLeaderElectionRecord.Strategy
        le.metrics.slowpathExercised(le.config.Name)
    } else {
        leaderElectionRecord.LeaderTransitions = oldLeaderElectionRecord.LeaderTransitions + 1
    }

    if err = le.config.Lock.Update(ctx, leaderElectionRecord); err != nil {
        logger.Error(err, "Failed to update lock")
        return false
    }

    le.setObservedRecord(&leaderElectionRecord)
    return true
}

协调模式 vs 标准模式差异
#mermaid-svg-SK4YMuYZBvygrjGw{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-SK4YMuYZBvygrjGw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SK4YMuYZBvygrjGw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SK4YMuYZBvygrjGw .error-icon{fill:#552222;}#mermaid-svg-SK4YMuYZBvygrjGw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SK4YMuYZBvygrjGw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SK4YMuYZBvygrjGw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SK4YMuYZBvygrjGw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SK4YMuYZBvygrjGw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SK4YMuYZBvygrjGw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SK4YMuYZBvygrjGw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SK4YMuYZBvygrjGw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SK4YMuYZBvygrjGw .marker.cross{stroke:#333333;}#mermaid-svg-SK4YMuYZBvygrjGw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SK4YMuYZBvygrjGw p{margin:0;}#mermaid-svg-SK4YMuYZBvygrjGw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SK4YMuYZBvygrjGw .cluster-label text{fill:#333;}#mermaid-svg-SK4YMuYZBvygrjGw .cluster-label span{color:#333;}#mermaid-svg-SK4YMuYZBvygrjGw .cluster-label span p{background-color:transparent;}#mermaid-svg-SK4YMuYZBvygrjGw .label text,#mermaid-svg-SK4YMuYZBvygrjGw span{fill:#333;color:#333;}#mermaid-svg-SK4YMuYZBvygrjGw .node rect,#mermaid-svg-SK4YMuYZBvygrjGw .node circle,#mermaid-svg-SK4YMuYZBvygrjGw .node ellipse,#mermaid-svg-SK4YMuYZBvygrjGw .node polygon,#mermaid-svg-SK4YMuYZBvygrjGw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SK4YMuYZBvygrjGw .rough-node .label text,#mermaid-svg-SK4YMuYZBvygrjGw .node .label text,#mermaid-svg-SK4YMuYZBvygrjGw .image-shape .label,#mermaid-svg-SK4YMuYZBvygrjGw .icon-shape .label{text-anchor:middle;}#mermaid-svg-SK4YMuYZBvygrjGw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SK4YMuYZBvygrjGw .rough-node .label,#mermaid-svg-SK4YMuYZBvygrjGw .node .label,#mermaid-svg-SK4YMuYZBvygrjGw .image-shape .label,#mermaid-svg-SK4YMuYZBvygrjGw .icon-shape .label{text-align:center;}#mermaid-svg-SK4YMuYZBvygrjGw .node.clickable{cursor:pointer;}#mermaid-svg-SK4YMuYZBvygrjGw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SK4YMuYZBvygrjGw .arrowheadPath{fill:#333333;}#mermaid-svg-SK4YMuYZBvygrjGw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SK4YMuYZBvygrjGw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SK4YMuYZBvygrjGw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SK4YMuYZBvygrjGw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SK4YMuYZBvygrjGw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SK4YMuYZBvygrjGw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SK4YMuYZBvygrjGw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SK4YMuYZBvygrjGw .cluster text{fill:#333;}#mermaid-svg-SK4YMuYZBvygrjGw .cluster span{color:#333;}#mermaid-svg-SK4YMuYZBvygrjGw 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-SK4YMuYZBvygrjGw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SK4YMuYZBvygrjGw rect.text{fill:none;stroke-width:0;}#mermaid-svg-SK4YMuYZBvygrjGw .icon-shape,#mermaid-svg-SK4YMuYZBvygrjGw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SK4YMuYZBvygrjGw .icon-shape p,#mermaid-svg-SK4YMuYZBvygrjGw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SK4YMuYZBvygrjGw .icon-shape .label rect,#mermaid-svg-SK4YMuYZBvygrjGw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SK4YMuYZBvygrjGw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SK4YMuYZBvygrjGw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SK4YMuYZBvygrjGw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 协调模式
tryCoordinatedRenew
无 Fast Path
Lease 不存在 → 直接返回 false
租约过期 → 返回 false
PreferredHolder ≠ '' → 停止续约
标准模式
tryAcquireOrRenew
Fast Path 乐观更新
Lease 不存在 → Create
租约过期 → 尝试获取

维度 标准模式 协调模式
Fast Path ✅ 乐观更新 ❌ 必须先 Get
Lease 不存在 Create 返回 false
自己不是领导者 可抢(租约过期时) 不可抢
PreferredHolder 不检查 检查 → 停止续约
Strategy 字段 不传递 传递
适用场景 竞争选举 协商选举(优雅切换)

3.7 release --- 主动释放租约

go 复制代码
func (le *LeaderElector) release(logger klog.Logger) bool {
    ctx := context.Background()
    // 使用 Background 而非传入的 ctx
    // 因为传入的 ctx 可能已经取消了
    // 但我们仍需要完成释放操作

    timeoutCtx, timeoutCancel := context.WithTimeout(ctx, le.config.RenewDeadline)
    defer timeoutCancel()
    // 设置超时,避免释放操作永久阻塞

    // ─── 获取当前 Lease 状态 ───
    oldLeaderElectionRecord, _, err := le.config.Lock.Get(timeoutCtx)
    if err != nil {
        if !errors.IsNotFound(err) {
            logger.Error(err, "error retrieving resource lock")
            return false
        }
        logger.Info("lease lock not found")
        return false
        // Lease 已不存在 → 无需释放
    }

    // ─── 检查自己是否仍是领导者 ───
    if !le.IsLeader() {
        return true
        // 不是领导者 → 无需释放
    }

    // ─── 构造释放记录 ───
    now := metav1.NewTime(le.clock.Now())
    leaderElectionRecord := rl.LeaderElectionRecord{
        LeaderTransitions:    oldLeaderElectionRecord.LeaderTransitions,
        // 保持转移次数
        LeaseDurationSeconds: 1,
        // 设为 1 秒 → 租约立即过期
        RenewTime:            now,
        AcquireTime:          now,
        // HolderIdentity 默认为零值(空字符串)
        // 空字符串 = 没有持有者 → 其他候选者可立即竞争
    }

    // ─── 更新 Lease ───
    if err := le.config.Lock.Update(timeoutCtx, leaderElectionRecord); err != nil {
        logger.Error(err, "Failed to release lease")
        return false
    }

    le.setObservedRecord(&leaderElectionRecord)
    return true
}

释放策略 :将 LeaseDurationSeconds 设为 1 且 HolderIdentity 清空,使其他候选者无需等待完整的 LeaseDuration 就能接管。

3.8 maybeReportTransition --- 领导权转移通知

go 复制代码
func (le *LeaderElector) maybeReportTransition() {
    // ─── 检查是否有变化 ───
    if le.observedRecord.HolderIdentity == le.reportedLeader {
        return
        // 观测到的领导者与已报告的相同 → 无需通知
    }

    // ─── 更新已报告的领导者 ───
    le.reportedLeader = le.observedRecord.HolderIdentity

    // ─── 异步通知 ───
    if le.config.Callbacks.OnNewLeader != nil {
        go le.config.Callbacks.OnNewLeader(le.reportedLeader)
        // 使用 goroutine 异步调用
        // 避免阻塞选举主循环
    }
}

3.9 isLeaseValid --- 租约有效性检查

go 复制代码
func (le *LeaderElector) isLeaseValid(now time.Time) bool {
    le.observedRecordLock.RLock()
    defer le.observedRecordLock.RUnlock()

    // 租约是否仍然有效:
    // observedTime + LeaseDurationSeconds > now
    // 即:从上次观测时间算起,租约还没过期
    return le.observedTime.Add(
        time.Second * time.Duration(le.observedRecord.LeaseDurationSeconds),
    ).After(now)
}

3.10 Check --- 健康检查

go 复制代码
func (le *LeaderElector) Check(maxTolerableExpiredLease time.Duration) error {
    // ─── 不是领导者 → 健康 ───
    if !le.IsLeader() {
        return nil
        // 非领导者不需要续约 → 无所谓健康
    }

    // ─── 是领导者 → 检查续约是否超时 ───
    le.observedRecordLock.RLock()
    lastObservation := le.observedTime
    leaseDuration := le.config.LeaseDuration
    le.observedRecordLock.RUnlock()

    // 如果距离上次观测已超过 LeaseDuration + 容忍时间
    // 说明续约已失败但进程未退出(可能是死锁)
    if le.clock.Since(lastObservation) > leaseDuration+maxTolerableExpiredLease {
        return fmt.Errorf("failed election to renew leadership on lease %s", le.config.Name)
    }

    return nil
}

健康检查的时间窗口
#mermaid-svg-bVXDIuCbSmJfMJXn{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-bVXDIuCbSmJfMJXn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bVXDIuCbSmJfMJXn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bVXDIuCbSmJfMJXn .error-icon{fill:#552222;}#mermaid-svg-bVXDIuCbSmJfMJXn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bVXDIuCbSmJfMJXn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bVXDIuCbSmJfMJXn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bVXDIuCbSmJfMJXn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bVXDIuCbSmJfMJXn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bVXDIuCbSmJfMJXn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bVXDIuCbSmJfMJXn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bVXDIuCbSmJfMJXn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bVXDIuCbSmJfMJXn .marker.cross{stroke:#333333;}#mermaid-svg-bVXDIuCbSmJfMJXn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bVXDIuCbSmJfMJXn p{margin:0;}#mermaid-svg-bVXDIuCbSmJfMJXn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bVXDIuCbSmJfMJXn .cluster-label text{fill:#333;}#mermaid-svg-bVXDIuCbSmJfMJXn .cluster-label span{color:#333;}#mermaid-svg-bVXDIuCbSmJfMJXn .cluster-label span p{background-color:transparent;}#mermaid-svg-bVXDIuCbSmJfMJXn .label text,#mermaid-svg-bVXDIuCbSmJfMJXn span{fill:#333;color:#333;}#mermaid-svg-bVXDIuCbSmJfMJXn .node rect,#mermaid-svg-bVXDIuCbSmJfMJXn .node circle,#mermaid-svg-bVXDIuCbSmJfMJXn .node ellipse,#mermaid-svg-bVXDIuCbSmJfMJXn .node polygon,#mermaid-svg-bVXDIuCbSmJfMJXn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bVXDIuCbSmJfMJXn .rough-node .label text,#mermaid-svg-bVXDIuCbSmJfMJXn .node .label text,#mermaid-svg-bVXDIuCbSmJfMJXn .image-shape .label,#mermaid-svg-bVXDIuCbSmJfMJXn .icon-shape .label{text-anchor:middle;}#mermaid-svg-bVXDIuCbSmJfMJXn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bVXDIuCbSmJfMJXn .rough-node .label,#mermaid-svg-bVXDIuCbSmJfMJXn .node .label,#mermaid-svg-bVXDIuCbSmJfMJXn .image-shape .label,#mermaid-svg-bVXDIuCbSmJfMJXn .icon-shape .label{text-align:center;}#mermaid-svg-bVXDIuCbSmJfMJXn .node.clickable{cursor:pointer;}#mermaid-svg-bVXDIuCbSmJfMJXn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bVXDIuCbSmJfMJXn .arrowheadPath{fill:#333333;}#mermaid-svg-bVXDIuCbSmJfMJXn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bVXDIuCbSmJfMJXn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bVXDIuCbSmJfMJXn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bVXDIuCbSmJfMJXn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bVXDIuCbSmJfMJXn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bVXDIuCbSmJfMJXn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bVXDIuCbSmJfMJXn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bVXDIuCbSmJfMJXn .cluster text{fill:#333;}#mermaid-svg-bVXDIuCbSmJfMJXn .cluster span{color:#333;}#mermaid-svg-bVXDIuCbSmJfMJXn 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-bVXDIuCbSmJfMJXn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bVXDIuCbSmJfMJXn rect.text{fill:none;stroke-width:0;}#mermaid-svg-bVXDIuCbSmJfMJXn .icon-shape,#mermaid-svg-bVXDIuCbSmJfMJXn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bVXDIuCbSmJfMJXn .icon-shape p,#mermaid-svg-bVXDIuCbSmJfMJXn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bVXDIuCbSmJfMJXn .icon-shape .label rect,#mermaid-svg-bVXDIuCbSmJfMJXn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bVXDIuCbSmJfMJXn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bVXDIuCbSmJfMJXn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bVXDIuCbSmJfMJXn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 健康状态
时间线
正常续约
仍在容忍窗口内
超过容忍窗口
observedTime
observedTime +

LeaseDuration
observedTime +

LeaseDuration +

maxTolerableExpired
健康 (Check returns nil)
不健康 (Check returns error)

相关推荐
牛奶咖啡132 小时前
k8s容器编排技术实践——K8s中服务发现ingress、ingress controller 应用实践
kubernetes·服务发现·ingress·ingress-nginx·部署ingress-nginx·部署ingress的三种方法·ingress的服务发现原理
张忠琳3 小时前
【client-go v0.36.1】tools/cache 深度分析(中篇)— 辅助组件逐行解析
云原生·kubernetes·cache·informer·client-go
张忠琳8 小时前
【client-go v0.36.1】WorkQueue 深度分析(下篇)— 限流队列、限流器、指标、并行化
云原生·kubernetes·informer·workqueue·client-go
张忠琳10 小时前
【client-go v0.36.1】WorkQueue 深度分析(上篇)— 模块定位、结构、基础队列与延迟队列
云原生·kubernetes·informer·workqueue·client-go
jieyucx10 小时前
站在云原生高并发天花板:拆解 Go 语言 GMP 模型与 I/O 多路复用的神级配合
开发语言·云原生·golang
张忠琳10 小时前
【client-go v0.36.1】tools/cache 深度分析(上篇)— 模块定位、整体结构、接口与依赖关系
云原生·kubernetes·cache·informer·client-go
张忠琳10 小时前
【client-go v0.36.1】(Reflector Part 1)Reflector 超深度分析 — 模块定位、整体结构、接口与依赖
云原生·kubernetes·informer·client-go·reflector
Demon1_Coder11 小时前
Day4-微服务-Seata
微服务·云原生·架构
张忠琳11 小时前
【client-go v0.36.1】client-go v0.36.1 系统级架构分析(下篇)
云原生·kubernetes·client-go