在实际生产中,特别是分布式系统中,我们经常遇到这样的场景:一个复杂的任务,近需要从分布式机器中选出一台机器来执行。诸如此类的问题,我们统称为“Master选举”。比如,在分布式系统中很常见的一个问题就是定时任务的执行。如果多台机器同时执行相同的定时任务,业务复杂则可能出现灾难性的后果。本篇博客就以定时任务为例来示例说明Curator的Master选举用法。
原理
利用zookeeper来实现Master选举的基本思路如下:
选择一个根节点(与其他业务隔离),比如/jobMaster,多台机器同时在此节点下面创建一个子节点/jobMaster/lock,zookeeper保证了最终只有一台机器能够创建成功,那么这台机器将成为Master。由它来执行业务操作。
Curator所做的事情就是将上面的思路进行了封装,把原生API的节点创建、事件监听和自动选举进行整合封装,提供了一套简单易用的解决方案。
添加Maven依赖
<dependencies>
...
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
...
</dependencies>
Curator提供了两种选举方案:Leader Latch和Leader Election。这里主要介绍下Leader Latch方案,Leader Latch 随机从候选着中选出一台作为leader,选中之后除非调用close()释放leadship,否则其他的后选择无法成为leader。
测试代码如下:
public class LeaderLatchTest {
private static final String PATH = "/demo/leader";
public static void main(String[] args) throws UnknownHostException {
LeaderLatch leaderLatch = null;
CuratorFramework client = null;
final String id = "client#" + InetAddress.getLocalHost().getHostAddress();
try {
client = getClient();
final LeaderLatch finalLeaderLatch = new LeaderLatch(client, PATH, id);
leaderLatch = finalLeaderLatch;
finalLeaderLatch.addListener(new LeaderLatchListener() {
@Override
public void isLeader() {
System.out.println(finalLeaderLatch.getId() + ":I am leader.");
}
@Override
public void notLeader() {
System.out.println(finalLeaderLatch.getId() + ":I am not leader.");
}
});
finalLeaderLatch.start();
Thread.sleep(Integer.MAX_VALUE);
} catch (Exception e) {
e.printStackTrace();
} finally {
CloseableUtils.closeQuietly(client);
CloseableUtils.closeQuietly(leaderLatch);
}
}
private static CuratorFramework getClient() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("192.168.110.201:2181,192.168.110.202:2181,192.168.110.203:2181") //zookeeper集群地址
.retryPolicy(retryPolicy)
.sessionTimeoutMs(6000)
.connectionTimeoutMs(3000)
.namespace("demo")
.build();
client.start();
return client;
}
}
机器A运行结果如下:
A机器先运行,被选主为leader。B机器后启动,没有收到消息,没有输出。
关闭服务器A的网络,A降级为从机
3秒后服务器B升级为主机
如果3内服务器A恢复网络,服务器A依然是leader。
版权声明:本文为chenxing109原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。