前言

在分布式系统中,我们常用 Lease(租约) 控制某个节点对资源的独占访问。然而,当网络分区或节点故障后,“过期”节点仍可能持有旧的访问权限并发起非法写入,这就引入了一个严重问题:脑裂(Split Brain)写入

为防止这种情况发生,Fencing(防越权机制) 被设计出来,作为 lease 的“护城河”,保障系统一致性与安全性。


定义

wiki 上未直接定义 fencing,但在分布式系统设计中,fencing 常被定义为:

Fencing is a mechanism to prevent outdated or unauthorized nodes from accessing a resource after their lease has expired or ownership has changed.

一句话:Fencing 是一种防止“过期节点”继续访问资源的保护机制。


工作原理

一句话:每次 lease 分配时生成一个递增的版本号,只有持有最新 fencing token 的节点才能访问资源。

典型流程如下:

  1. 每次 lease 分配/续约时,协调者生成一个唯一递增的 fencing token
  2. 节点在访问资源时必须携带该 token;
  3. 资源服务端(如存储系统)在处理请求前检查 token 是否为最新;
  4. 如果 token 过期或无效,拒绝处理,防止旧节点写入。

此机制不依赖时钟同步,天然抗网络分区,是解决“旧节点写穿”问题的关键。


属性与指标

关注的 fencing 属性与指标如下:

  1. Fencing Token 单调递增性 保证每次 token 都比上一次大,避免重用。

  2. Token 分发机制 通常由中心协调者(如 etcd/Zookeeper)统一生成和分发。

  3. 资源端校验能力 后端必须支持对 token 的解析与校验逻辑。

  4. 防止 ABA 问题 防止“旧 lease -> 新 lease -> 旧 lease”导致错误重入。

  5. 无依赖于时钟精度 fencing token 本质为逻辑版本号,不依赖机器时钟。


类型

根据 token 类型分

  1. 数值型 token(如递增整数) 最常见方式,每次 lease 分配时递增。例如 etcd 使用 revision。

  2. 时间戳型 token 使用分配时刻作为 token,但需注意时钟漂移问题。

  3. UUID型(唯一标识符) 虽不能比较新旧,但可用于检测不同实例。

根据校验策略分

  1. 客户端校验型 客户端先读出当前 token,再判断是否自己仍持有有效权限。

  2. 服务端校验型(推荐) 服务端保存最新 token,拒绝处理老 token 的请求。


fencing 与程序关系

程序中使用 fencing 的方式通常如下:

  1. 分布式锁服务结合 fencing 比如基于 Zookeeper ephemeral node + fencing token 控制写入权。

  2. 存储系统防止旧主写入 比如 HDFS 的 NameNode 使用 fencing 来保护数据块元数据写入。

  3. 任务调度系统防止重复执行 调度器判断执行者是否仍持有有效 lease token,防止旧任务重复执行。

编程实践要点:

  • 资源服务端必须内建 fencing token 校验逻辑;
  • 在 lease 获取时记录 token,所有访问必须带 token;
  • 注意 token 回收与过期处理,防止内存泄漏。

常用 fencing 场景/产品

  1. etcd + fencing token etcd 事务 revision 号可作为 fencing token,天然递增。

  2. Zookeeper + 顺序节点 通过顺序 ephemeral node 实现 fencing,最小序号即为持有者。

  3. HDFS + fencing script Secondary NameNode 激活前执行 fencing 脚本(如强制 unmount)。

  4. Kubernetes Lease API + fencing controller-manager 使用 fencing 确保只有最新 leader 可操作资源。

  5. Google Chubby Lock Service 分配 lock ID 作为 fencing token,用于保证唯一写者。