文章目录
- 前言
- Curator框架源码地址
- 核心本质原理
- 源码解析
-
- LeaderLatch核心实现(选主逻辑与监听器)
-
- [1、初始化与启动(LeaderLatch构造器 & start)](#1、初始化与启动(LeaderLatch构造器 & start))
- 2、监听器(`LeaderLatchListener`)机制
- 3、关闭逻辑
- 连接状态监听(`ConnectionStateListener`)
- 资料获取

前言
博主介绍:✌目前全网粉丝4W+,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。
涵盖技术内容:Java后端、大数据、算法、分布式微服务、中间件、前端、运维等。
博主所有博客文件目录索引:博客目录索引(持续更新)
CSDN搜索:长路
视频平台:b站-Coder长路
Curator框架源码地址
github地址:https://github.com/apache/curator
我们切到apache-curator-3.3.0这个tag。
核心本质原理
本质 :利用 ZooKeeper 的临时顺序节点 + 节点序号最小者获胜 机制。
优点设计:
临时节点:客户端断开时自动删除,避免僵尸节点
顺序节点:保证每个客户端获得唯一递增序号
最小序号获胜:公平且确定性的选举算法
监听机制:每个客户端只监听紧挨着自己的前一个节点,避免羊群效应。
plain
[初始状态]
/election/lock-0000000001 (客户端A) ← Leader
/election/lock-0000000002 (客户端B) ← 监听 lock-0000000001
/election/lock-0000000003 (客户端C) ← 监听 lock-0000000002
[客户端A下线后]
/election/lock-0000000002 (客户端B) ← 新Leader
/election/lock-0000000003 (客户端C) ← 监听 lock-0000000002
Curator 主从选举最核心的原理:利用 ZooKeeper 临时顺序节点的序号特性,序号最小者获胜,其他节点监听前一个节点的删除事件。
第1步:客户端启动,创建临时顺序节点
java
// 每个客户端执行:
client.create()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.forPath("/leader/latch-", id.getBytes());
实际创建的节点:
java
/leader/latch-0000000001 (客户端A)
/leader/latch-0000000002 (客户端B)
/leader/latch-0000000003 (客户端C)

第2步:获取所有子节点并排序
java
List<String> children = client.getChildren().forPath("/leader");
// 排序后:
["latch-0000000001", "latch-0000000002", "latch-0000000003"]
第3步:检查自己是否是第一个节点
客户端A (latch-0000000001):
- 自己的索引 = 0 (第一个)
- ✅ 成为 Leader
客户端B (latch-0000000002):
- 自己的索引 = 1 (第二个)
- ❌ 不是 Leader,需要监听前一个节点
latch-0000000001
客户端C (latch-0000000003):
- 自己的索引 = 2 (第三个)
- ❌ 不是 Leader,需要监听前一个节点
latch-0000000002
核心代码如下:

第4步:领导权变更
当客户端A下线时:
- ZooKeeper 自动删除临时节点
latch-0000000001 - 客户端B 收到 NodeDeleted 通知
客户端B 重新检查排序:
java
// 新的子节点列表:
["latch-0000000002", "latch-0000000003"]
- 客户端B 索引变为 0 → ✅ 成为新 Leader

源码解析
LeaderLatch核心实现(选主逻辑与监听器)
LeaderLatch是 Curator 实现分布式选主的核心类,主要逻辑在LeaderLatch.java中
1、初始化与启动(LeaderLatch构造器 & start)
java
// LeaderLatch构造方法
public LeaderLatch(CuratorFramework client, String latchPath, String id, CloseMode closeMode) {
this.client = client.newWatcherRemoveCuratorFramework(); // 包装客户端,用于移除监听器
this.latchPath = PathUtils.validatePath(latchPath); // 校验选主节点路径
this.id = id; // 节点标识(如你的localAddress)
this.closeMode = closeMode;
}
// 启动选主逻辑
public void start() throws Exception {
// 状态校验:只能从LATENT转为STARTED
Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once");
// 连接建立后执行内部启动逻辑
startTask.set(AfterConnectionEstablished.execute(client, new Runnable() {
@Override
public void run() {
try {
internalStart(); // 核心选主逻辑(创建临时节点、监控节点变化等)
} finally {
startTask.set(null);
}
}
}));
}
- 启动后会在
latchPath下创建临时顺序节点(如/latchPath/latch-000000001),通过节点顺序判断leader。 - 临时节点特性:当客户端与ZK断开连接(会话失效)时,节点会自动删除,触发重新选主。
继续看下internalStart方法:
java
private synchronized void internalStart() {
if (state.get() == State.STARTED) {
// 3. 添加连接状态监听器
client.getConnectionStateListenable().addListener(listener);
try {
// 4. 开始参与选举
reset();
} catch (Exception e) {
ThreadUtils.checkInterrupted(e);
log.error("An error occurred checking resetting leadership.", e);
}
}
}
// 对于添加连接状态监听器实现,其自己本身就实现了一个listener
private final ConnectionStateListener listener = new ConnectionStateListener(){
@Override
public void stateChanged(CuratorFramework client, ConnectionState newState)
{
handleStateChange(newState);
}
};
// 该部分变更状态来源为:ConnectionStateManager#processEvents 会进行状态的检测变更
// 处理状态的变更情况
private void handleStateChange(ConnectionState newState)
{
switch ( newState )
{
default:
{
// NOP
break;
}
case RECONNECTED:
{
try
{
if ( client.getConnectionStateErrorPolicy().isErrorState(ConnectionState.SUSPENDED) || !hasLeadership.get() )
{
reset();
}
}
catch ( Exception e )
{
ThreadUtils.checkInterrupted(e);
log.error("Could not reset leader latch", e);
setLeadership(false);
}
break;
}
case SUSPENDED:
{
if ( client.getConnectionStateErrorPolicy().isErrorState(ConnectionState.SUSPENDED) )
{
setLeadership(false);
}
break;
}
case LOST:
{
setLeadership(false);
break;
}
}
}
**核心选举逻辑:**reset()
java
void reset() throws Exception {
// 5. 放弃领导权
setLeadership(false);
// 6. 删除之前创建的节点
setNode(null);
BackgroundCallback callback = new BackgroundCallback() {
@Override
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
if (event.getResultCode() == KeeperException.Code.OK.intValue()) {
// 7. 创建临时顺序节点
setNode(event.getName());
if (state.get() == State.CLOSED) {
setNode(null);
} else {
// 8. 获取所有子节点,检查是否成为 Leader
getChildren();
}
} else {
log.error("getChildren() failed. rc = " + event.getResultCode());
}
}
};
// 9. 创建临时顺序节点
client.create()
.creatingParentContainersIfNeeded()
.withProtection()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.inBackground(callback)
.forPath(ZKPaths.makePath(latchPath, LOCK_NAME),
LeaderSelector.getIdBytes(id));
}
检查是否是leadership:
java
private void checkLeadership(List<String> children) throws Exception
{
final String localOurPath = ourPath.get();
// 10. 对所有子节点排序
List<String> sortedChildren = LockInternals.getSortedChildren(LOCK_NAME, sorter, children);
// 11. 找到自己节点的序号
int ourIndex = (localOurPath != null) ? sortedChildren.indexOf(ZKPaths.getNodeFromPath(localOurPath)) : -1;
if ( ourIndex < 0 )
{
log.error("Can't find our node. Resetting. Index: " + ourIndex);
// 12. 如果找不到自己的节点,重新参与选举
reset();
}
else if ( ourIndex == 0 )
{
// 13. 如果自己是第一个节点,获得领导权
setLeadership(true);
}
else
{
// 14. 否则监听前一个节点
String watchPath = sortedChildren.get(ourIndex - 1);
Watcher watcher = new Watcher()
{
@Override
public void process(WatchedEvent event)
{
if ( (state.get() == State.STARTED) && (event.getType() == Event.EventType.NodeDeleted) && (localOurPath != null) )
{
try
{
// 15. 前一个节点被删除,重新检查
getChildren();
}
catch ( Exception ex )
{
ThreadUtils.checkInterrupted(ex);
log.error("An error occurred checking the leadership.", ex);
}
}
}
};
BackgroundCallback callback = new BackgroundCallback()
{
@Override
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception
{
if ( event.getResultCode() == KeeperException.Code.NONODE.intValue() )
{
// previous node is gone - reset
reset();
}
}
};
// 16. 监听前一个节点
// use getData() instead of exists() to avoid leaving unneeded watchers which is a type of resource leak
client.getData().usingWatcher(watcher).inBackground(callback).forPath(ZKPaths.makePath(latchPath, watchPath));
}
}
2、监听器(LeaderLatchListener)机制
java
// LeaderLatch中管理监听器的成员
private final StandardListenerManager<LeaderLatchListener> listeners = StandardListenerManager.standard();
// 添加监听器
public void addListener(LeaderLatchListener listener) {
listeners.addListener(listener);
}
// 触发监听器回调(内部逻辑,当选主状态变化时调用)
private void setLeadership(boolean newValue) {
boolean oldValue = hasLeadership.getAndSet(newValue);
if (oldValue != newValue) {
if (newValue) {
// 成为leader时回调isLeader()
listeners.forEach(listener -> listener.isLeader());
} else {
// 失去leader时回调notLeader()
listeners.forEach(listener -> listener.notLeader());
}
}
}
其中对于setLeaderShip操作涉及到了如下几个方法调用来源:

- 当节点顺序变化(如leader节点删除),
LeaderLatch会重新判断自身是否为leader,通过setLeadership触发监听器的isLeader()或notLeader()。
3、关闭逻辑
java
@Override
public void close() throws IOException {
close(closeMode); // 默认使用构造时的CloseMode(如SILENT)
}
private synchronized void internalClose(CloseMode closeMode, boolean failOnClosed) throws IOException {
if (!state.compareAndSet(State.STARTED, State.CLOSED)) {
if (failOnClosed) {
throw new IllegalStateException("Already closed or has not been started");
}
return;
}
cancelStartTask(); // 取消启动任务
try {
setNode(null); // 删除自身创建的临时节点
client.removeWatchers(); // 移除ZK监听器
} catch (Exception e) {
throw new IOException(e);
} finally {
// 移除连接状态监听器
client.getConnectionStateListenable().removeListener(listener);
// 根据CloseMode处理监听器(如通知失去 leadership)
switch (closeMode) {
case NOTIFY_LEADER:
setLeadership(false);
listeners.clear();
break;
case SILENT:
listeners.clear();
setLeadership(false);
break;
}
}
}
连接状态监听(ConnectionStateListener)
Curator的CuratorFramework通过getConnectionStateListenable()提供连接状态监听能力,底层实现涉及如下。
1、连接状态监听器的注册与触发
java
// CuratorFrameworkImpl中获取连接状态监听器容器
@Override
public Listenable<ConnectionStateListener> getConnectionStateListenable() {
return connectionStateManager.getListenable();
}
// DelegatingCuratorFramework(代理类)同样支持
@Override
public Listenable<ConnectionStateListener> getConnectionStateListenable() {
return client.getConnectionStateListenable();
}
Listenable是Curator的监听器容器接口,支持添加/移除监听器。- 当ZK连接状态变化(如
SUSPENDED/LOST/RECONNECTED)时,会遍历所有注册的ConnectionStateListener并触发stateChanged方法。
2、LeaderLatch内部的连接状态处理
LeaderLatch自身也注册了连接状态监听器,用于处理连接异常:
java
private final ConnectionStateListener listener = new ConnectionStateListener() {
@Override
public void stateChanged(CuratorFramework client, ConnectionState newState) {
handleStateChange(newState); // 处理连接状态变化
}
};
// 连接状态变化时的处理逻辑(内部方法)
private void handleStateChange(ConnectionState newState) {
switch (newState) {
case SUSPENDED:
case LOST:
// 连接中断时,标记为非leader
setLeadership(false);
break;
case RECONNECTED:
// 重连后重新参与选主
try {
reset(); // 重置状态并重新创建节点
} catch (Exception e) {
log.error("Error resetting leader latch after reconnection", e);
}
break;
// 其他状态忽略
default:
break;
}
}
资料获取
大家点赞、收藏、关注、评论啦~
精彩专栏推荐订阅:在下方专栏👇🏻
- 长路-文章目录汇总(算法、后端Java、前端、运维技术导航):博主所有博客导航索引汇总
- 开源项目Studio-Vue---校园工作室管理系统(含前后台,SpringBoot+Vue):博主个人独立项目,包含详细部署上线视频,已开源
- 学习与生活-专栏:可以了解博主的学习历程
- 算法专栏:算法收录
更多博客与资料可查看👇🏻获取联系方式👇🏻,🍅文末获取开发资源及更多资源博客获取🍅