应用场景
在分布式项目开发中,我们通常需要将某个程序部署到多个节点上面。但是要求程序的运行方式是一主多从,当主节点Master挂掉后,可以从剩余的节点中从新选举出新的Master节点来继续工作,对于Leader的选举策略,Apache Curator框架提供了两种策略,开发者可以根据实际需求具体选择:
策略1:LeaderLatch 参考:https://curator.apache.org/curator-recipes/leader-latch.html
简要概括:这种选举方式是保持Leader一直当到底,除非调用close方法,才会进行重新选举,因此这种方式适合不需要频繁切换Leader的情况;
策略2:LeaderElection 参考:https://curator.apache.org/curator-recipes/leader-election.html
简要概括:这种选举方式是会让众多的节点轮流当Leader,通俗来说就是轮流做大哥,当LeaderSelectorListenerAdapter类中的takeLeadership方法执行完毕,Leader就会释放当Leader的权利,然后重新竞选新的Leader,这就保证了,大家都有做大哥的机会,因此这种方式适合需要频繁切换Leader的情况;
添加依赖
compile group: 'org.apache.curator', name: 'curator-recipes', version: '4.2.0'
策略1:LeaderLatch (初始化多个LeaderLatch,然后自动选出Leader) 代码展示
public class MyLeaderLatch {
private String name; // Latch名称
private LeaderLatch leaderLatch;
private LeaderTask leaderTask; // Leader当选之后需要做的事情
private String PATH = "/latchPath"; // Latch Path
public MyLeaderLatch(CuratorFramework client, String name, LeaderTask leaderTask) {
this.name = name;
this.leaderTask = leaderTask;
this.leaderLatch = new LeaderLatch(client, PATH); //初始化LeaderLatch
}
//创建任务接口,外部提供实现
public interface LeaderTask {
public void doTASK();
}
//自定义LeaderLatch监听器
private class MyLeaderLatchListener implements LeaderLatchListener {
private String name;
private LeaderTask leaderTask;
public MyLeaderLatchListener(String name, LeaderTask leaderTask) {
this.name = name;
this.leaderTask = leaderTask;
}
@Override
public void isLeader() {
System.out.println(name + " say : I am become a leader!");
this.leaderTask.doTASK();
}
@Override
public void notLeader() {
// do something
}
}
//启动LeaderLatch
public void start() throws Exception {
leaderLatch.start();
//为LeaderLatch添加监听器
leaderLatch.addListener(new MyLeaderLatchListener(this.name, this.leaderTask));
}
//关闭LeaderLatch,释放Leader权力
public void close() throws Exception {
System.out.println(name + " say : I am no longer a leader!");
leaderLatch.close();
}
}
- 测试代码
public class MyTest {
CuratorFramework client = null;
//测试之前初始化CuratorFramework
@Before
public void beforeTest() {
String zkServerAddress = "127.0.0.1:2181";
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3, 5000);
client = CuratorFrameworkFactory.builder().connectString(zkServerAddress).sessionTimeoutMs(5000)
.connectionTimeoutMs(5000).retryPolicy(retryPolicy).build();
client.start();
}
//创建5个LeaderLatch竞选Leader
@Test
public void testMyLeaderLatch() throws Exception {
for (int i = 1; i <= 5; i++) {
MyLeaderLatch selector = new MyLeaderLatch(client, "Latch-" + i, new MyLeaderLatch.LeaderTask() {
@Override
public void doTASK() {
System.out.println("Leader do something!");
}
});
selector.start();
//等1S,调用close放弃主权
Thread.sleep(1000);
selector.close();
}
//延时1分支看输出结果
Thread.sleep(60000);
}
}
- 测试结果(从结果可以看出,放弃主权之后的节点不再参与选举)
Latch-1 say : I am become a leader!
Leader do something!
Latch-1 say : I am no longer a leader!
Latch-2 say : I am become a leader!
Leader do something!
Latch-2 say : I am no longer a leader!
Latch-3 say : I am become a leader!
Leader do something!
Latch-3 say : I am no longer a leader!
Latch-4 say : I am become a leader!
Leader do something!
Latch-4 say : I am no longer a leader!
Latch-5 say : I am become a leader!
Leader do something!
Latch-5 say : I am no longer a leader!
策略2:LeaderElection(初始化多个LeaderSelector,然后自动选出Leader) 代码展示
public class MyLeaderElection extends LeaderSelectorListenerAdapter implements Closeable {
private final String name;
private final LeaderTask leaderTask;
private final LeaderSelector leaderSelector;
private String PATH = "/latchPath"; // Latch Path
public MyLeaderElection(CuratorFramework client, String name, LeaderTask leaderTask) {
this.name = name;
this.leaderTask = leaderTask;
//初始化LeaderSelector
this.leaderSelector = new LeaderSelector(client, PATH, this);
//放弃主权后自动重新参与竞选
this.leaderSelector.autoRequeue();
}
public interface LeaderTask {
public void doTASK();
}
public void start() throws Exception {
leaderSelector.start();
}
public void close() {
leaderSelector.close();
}
@Override
public void takeLeadership(CuratorFramework client) throws Exception {
System.out.println(name + " say : I am become a leader!");
try {
this.leaderTask.doTASK();
} finally {
System.out.println(name + " say : I am no longer a leader!");
}
}
}
- 测试代码
public class MyTest {
CuratorFramework client = null;
private List<MyLeaderElection> selectorList = new ArrayList<>(5);
@Before
public void beforeTest() {
String zkServerAddress = "127.0.0.1:2181";
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3, 5000);
client = CuratorFrameworkFactory.builder().connectString(zkServerAddress).sessionTimeoutMs(5000)
.connectionTimeoutMs(5000).retryPolicy(retryPolicy).build();
client.start();
}
@Test
public void testMyLeaderElection() throws Exception {
for (int i = 1; i <= 5; i++) {
MyLeaderElection selector = new MyLeaderElection(client, "Latch-" + i, new MyLeaderElection.LeaderTask() {
@Override
public void doTASK() {
System.out.println("Leader do something!");
}
});
selector.start();
selectorList.add(selector);
}
Thread.sleep(60000);
}
@After
public void afterTest() {
selectorList.forEach(selector -> selector.close());
}
}
- 测试结果(可以看出,这种选举模式leader自动切换,并且是随机的)
Latch-3 say : I am no longer a leader!
Latch-2 say : I am become a leader!
Leader do something!
Latch-2 say : I am no longer a leader!
Latch-4 say : I am become a leader!
Leader do something!
Latch-4 say : I am no longer a leader!
Latch-5 say : I am become a leader!
Leader do something!
Latch-5 say : I am no longer a leader!
Latch-1 say : I am become a leader!
Leader do something!
Latch-1 say : I am no longer a leader!
Latch-3 say : I am become a leader!
Leader do something!
Latch-3 say : I am no longer a leader!
Latch-2 say : I am become a leader!
Leader do something!
Latch-2 say : I am no longer a leader!
Latch-4 say : I am become a leader!
Leader do something!
Latch-4 say : I am no longer a leader!
Latch-5 say : I am become a leader!
Leader do something!
Latch-5 say : I am no longer a leader!
Latch-1 say : I am become a leader!
Leader do something!
Latch-1 say : I am no longer a leader!
....
....
....