1、最小节点获胜
1. 设计 Path
集群共用父节点 parent znode,集群中的每个节点在 parent 目录下创建自己的 znode。
2. 选择节点类型
当 Leader 节点挂掉的时候,持有最小编号 znode 的集群节点成为新的 Leader,因此用
ephemeral_sequential
类型 znode。
3. 设计节点数据
可以根据业务需要灵活写入各种数据。
4. 设计 Watch
1. 节点启动或者重连后,在 parent 目录下创建自己的 ephemeral_sequntial znode;
2. 创建成功后扫描 parent 目录下所有 znode,如果自己的 znode 编号是最小的,则成为
Leader,否则 watch parent 目录;
3. 当 parent 目录有节点删除的时候,首先判断其是否是 Leader 节点,然后再看其编号是否
正好比自己小1,如果是则自己成为 Leader,如果不是继续 watch。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
</dependencies>
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
@SpringBootApplication
public class ZooKeeperLeaderElectionApplication implements Watcher {
private static final String ZOOKEEPER_CONNECTION_STRING = "localhost:2181";
private static final int SESSION_TIMEOUT = 3000;
private static final String ELECTION_NAMESPACE = "/election";
@Autowired
private ZooKeeper zooKeeper;
@Bean
public ZooKeeper zooKeeper() throws IOException {
return new ZooKeeper(ZOOKEEPER_CONNECTION_STRING, SESSION_TIMEOUT, this);
}
@Override
public void process(WatchedEvent event) {
// Process ZooKeeper events
}
public void createElectionNode() throws KeeperException, InterruptedException {
Stat stat = zooKeeper.exists(ELECTION_NAMESPACE, false);
if (stat == null) {
zooKeeper.create(ELECTION_NAMESPACE, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
public void joinElection() throws KeeperException, InterruptedException {
String currentNodePath = zooKeeper.create(ELECTION_NAMESPACE + "/node", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("Joined election with path: " + currentNodePath);
}
public void electLeader() throws KeeperException, InterruptedException {
List<String> allNodes = zooKeeper.getChildren(ELECTION_NAMESPACE, false);
Collections.sort(allNodes);
String smallestNode = allNodes.get(0);
String currentNode = currentNodePath.substring(currentNodePath.lastIndexOf("/") + 1);
if (currentNode.equals(smallestNode)) {
System.out.println("I am the leader!");
// TODO: Execute leader-specific logic here
} else {
System.out.println("I am not the leader. Waiting for the leader to emerge...");
}
}
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
SpringApplication.run(ZooKeeperLeaderElectionApplication.class, args);
}
}
2、抢建唯一节点
1. 设计 Path
集群所有节点只有一个 leader znode,本质上就是一个分布式锁。
2. 选择 znode 类型
当 Leader 节点挂掉的时候,剩余节点都来创建 leader znode,看谁
能最终抢到 leader znode,因此用
ephemeral
类型。
3. 设计节点数据
可以根据业务需要灵活写入各种数据。
4. 设计 Watch
1. 节点启动或者重连后,尝试创建 leader znode,尝试失败则
watch leader znode;
2. 当收到 leader znode 被删除的事件通知后,再次尝试创建 leader
znode,尝试成功则成为 leader ,失败则 watch leader znode。
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
@SpringBootApplication
public class ZooKeeperExclusiveNodeApplication implements Watcher {
private static final String ZOOKEEPER_CONNECTION_STRING = "localhost:2181";
private static final int SESSION_TIMEOUT = 3000;
private static final String EXCLUSIVE_NODE_PATH = "/exclusive_node";
@Autowired
private ZooKeeper zooKeeper;
@Bean
public ZooKeeper zooKeeper() throws IOException {
return new ZooKeeper(ZOOKEEPER_CONNECTION_STRING, SESSION_TIMEOUT, this);
}
@Override
public void process(WatchedEvent event) {
// Process ZooKeeper events
}
public void createExclusiveNode() throws KeeperException, InterruptedException {
Stat stat = zooKeeper.exists(EXCLUSIVE_NODE_PATH, false);
if (stat == null) {
zooKeeper.create(EXCLUSIVE_NODE_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
public boolean tryAcquireExclusiveNode() throws KeeperException, InterruptedException {
String nodePath = zooKeeper.create(EXCLUSIVE_NODE_PATH + "/lock", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
// Get all children and check if this node has the smallest sequence number
String[] children = zooKeeper.getChildren(EXCLUSIVE_NODE_PATH, false).toArray(new String[0]);
String thisNode = nodePath.substring(nodePath.lastIndexOf("/") + 1);
int thisNodeSequence = Integer.parseInt(thisNode.substring(thisNode.length() - 10));
boolean isExclusiveNode = true;
for (String child : children) {
int childSequence = Integer.parseInt(child.substring(child.length() - 10));
if (childSequence < thisNodeSequence) {
isExclusiveNode = false;
break;
}
}
return isExclusiveNode;
}
public void doWorkAsLeader() throws InterruptedException {
System.out.println("I am the leader! Performing leader-specific tasks...");
// TODO: Execute leader-specific logic here
// Sleep for a while to simulate leader work
Thread.sleep(5000);
}
public void waitForLeader() throws KeeperException, InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
while (true) {
String[] children = zooKeeper.getChildren(EXCLUSIVE_NODE_PATH, new LeaderWatcher(latch));
if (children.length == 0) {
System.out.println("Waiting for the leader to establish...");
latch.await();
} else {
System.out.println("Leader is already established. Starting as a follower...");
break;
}
}
}
}
4、法官判决
1. 设计Path
集群共用父节点 parent znode,集群中的每个节点在 parent 目录下创建自己的 znode。
2. 选择节点类型
当 Leader 节点挂掉的时候,持有最小编号 znode 的集群节点成为
“法官”
,因此用
ephemeral_sequential
类型
znode。
3. 设计节点数据
可以根据业务需要灵活写入各种数据,例如写入当前存储的最新的数据对应的事务 ID。
4. 设计Watch
1. 节点启动或者重连后,在 parent 目录下创建自己的
ephemeral_sequntial
znode,并 watch
parent 目录;
2. 当 parent 目录有节点删除的时候,所有节点更新自己的 znode 里面和选举相关的数据;
3. “法官”节点读取所有 znode 的数据,根据规则或者算法选举新的 Leader,将选举结果写入 parent
znode;
4. 所有节点 watch parent znode,收到变更通知的时候读取 parent znode 的数据,发现是自己则成
为 Leader。