本文介绍zk客户端curator的使用,本文主要介绍Curator recipes的使用
官方文档传送门
官方文档Curator recipes
参考:http://www.throwable.club/2018/12/16/zookeeper-curator-usage/
https://blog.csdn.net/hosaos/article/details/88727817
参考书籍:《从Paxos到ZooKeeper 分布式一致性原理与实践》
1. Master选举
再多台机器中选择一个Master,利用ZK的特性。多台机器同时向一个节点创建子节点,最终只会有一个成功,那么他就是Master。有两种实现方式:LeaderSelector和LeaderLatch
pom
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.13.0</version>
</dependency>
2. LeaderLatch
原理
指定一个根路径,例如/leader_latch
,多个机器同时向该根路径下创建临时顺序节点,/leader_latch/node_1
,/leader_latch/node_2
,/leader_latch/node_3
,,/leader_latch/node_4
,编号最小node_1对应的客户端成为leader,其他节点都监听前一个节点的删除事件,在前一个节点删除后进行重新抢主。
关键方法
// 开始抢主
void start()
// 释放leader权限
void close()
// 阻塞线程,尝试获取leader权限,但不一定成功,超时失败
boolean await(long, java.util.concurrent.TimeUnit)
// 判断是否拥有leader权限
boolean hasLeadership()
事例
import com.google.common.collect.Lists;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
/**
* Master选择
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class MasterApiTests {
private CuratorFramework client;
/**
* 使用Fluent风格API创建
*/
@Before
public void client() {
// 重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
client = CuratorFrameworkFactory.builder()
.connectString("localhost:2181")
.sessionTimeoutMs(5000) // 会话超时时间,默认60000ms
.connectionTimeoutMs(10000) // 连接超时时间,默认15000ms
.retryPolicy(retryPolicy) // 重试策略
.namespace("base") // 名称空间,如果这里设置了名称空间,那么所有路径都将预先指定名称空间
.build();
client.start();
}
/**
* LeaderLatch
*/
@Test
public void LeaderLatch() throws Exception {
// 客户端数
int CLINET_COUNT = 10;
String path = "/latch-path";
List<CuratorFramework> clientsList = Lists.newArrayListWithCapacity(CLINET_COUNT);
List<LeaderLatch> latchList = Lists.newArrayListWithCapacity(CLINET_COUNT);
// 创建10个zk客户端模拟leader选举
for (int i = 0; i < CLINET_COUNT; i++) {
CuratorFramework client = getZkClient();
clientsList.add(client);
// 参数3:客户端ID
// 参数4:关闭策略,SILENT-关闭时不触发监听器回调,NOTIFY_LEADER-关闭时触发监听器回调方法,默认不触发
LeaderLatch leaderLatch = new LeaderLatch(client, path, "CLIENT_" + i);
latchList.add(leaderLatch);
// 调用start方法开始抢主
leaderLatch.start();
}
// 判断当前leader是哪个客户端
checkLeader(latchList);
}
private void checkLeader(List<LeaderLatch> leaderLatchList) throws Exception {
// 睡5秒
Thread.sleep(5000);
for (LeaderLatch leaderLatch : leaderLatchList) {
// 判断当前客户端是否是leader
if (leaderLatch.hasLeadership()) {
System.out.println("当前leader:" + leaderLatch.getId());
// 释放leader权限
leaderLatch.close();
// 检查当前leader
checkLeader(leaderLatchList);
}
}
}
private CuratorFramework getZkClient() {
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3, 5000);
CuratorFramework zkClient = CuratorFrameworkFactory.builder()
.connectString("localhost:2181")
.sessionTimeoutMs(5000) // 会话超时时间,默认60000ms
.connectionTimeoutMs(10000) // 连接超时时间,默认15000ms
.retryPolicy(retryPolicy) // 重试策略
.build();
zkClient.start();
return zkClient;
}
}
控制台
3. LeaderSelector
原理
利用Curator中InterProcessMutex分布式锁进行抢主,抢到锁的的为Leader。
关键方法
// 开始抢主
void start()
// 在释放leader权限后,自动加入抢主队列,重新抢主
void autoRequeue()
事例
import com.google.common.collect.Lists;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderLatch;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
/**
* Master选择
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class MasterApiTests {
private CuratorFramework client;
/**
* 使用Fluent风格API创建
*/
@Before
public void client() {
// 重试策略
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
client = CuratorFrameworkFactory.builder()
.connectString("localhost:2181")
.sessionTimeoutMs(5000) // 会话超时时间,默认60000ms
.connectionTimeoutMs(10000) // 连接超时时间,默认15000ms
.retryPolicy(retryPolicy) // 重试策略
.namespace("base") // 名称空间,如果这里设置了名称空间,那么所有路径都将预先指定名称空间
.build();
client.start();
}
/**
* LeaderSelector
*/
@Test
public void LeaderSelector() throws Exception {
String path = "/master-path";
int CLINET_COUNT = 10;
List<CuratorFramework> clientsList = Lists.newArrayListWithCapacity(CLINET_COUNT);
List<LeaderSelector> latchList = Lists.newArrayListWithCapacity(CLINET_COUNT);
// 创建10个zk客户端模拟leader选举
for (int i = 0; i < CLINET_COUNT; i++) {
CuratorFramework client = getZkClient();
clientsList.add(client);
// 可以多个参数3:ExecutorService: Master选举单独使用的线程池
// public LeaderSelector(CuratorFramework client, String leaderPath, ExecutorService executorService, LeaderSelectorListener listener)
LeaderSelector selector = new LeaderSelector(client, path,
new LeaderSelectorListenerAdapter() {
public void takeLeadership(CuratorFramework client) throws Exception {
System.out.println(Thread.currentThread().getName() + "成为了Master");
Thread.sleep(3000);
System.out.println("释放Master权利");
}
});
latchList.add(selector);
}
latchList.forEach(leaderSelector -> {
// 重新获得竞争权利
leaderSelector.autoRequeue();
// 启动选举
leaderSelector.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread.sleep(Integer.MAX_VALUE);
}
private CuratorFramework getZkClient() {
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3, 5000);
CuratorFramework zkClient = CuratorFrameworkFactory.builder()
.connectString("localhost:2181")
.sessionTimeoutMs(5000) // 会话超时时间,默认60000ms
.connectionTimeoutMs(10000) // 连接超时时间,默认15000ms
.retryPolicy(retryPolicy) // 重试策略
.build();
zkClient.start();
return zkClient;
}
}
控制台
4. 区别
- 实现原理略有不同,不过Curator中InterProcessMutex分布式锁的实现原理同LeaderLatch非常相似。
- LeaderSelector处理完业务逻辑后会自动释放锁,并且可以重新获取。LeaderLatch获取一次就不能再获取了。
源码地址
IT-CLOUD-ZOOKEEPER :zookeeper客户端Curator事例
项目推荐
IT-CLOUD :IT服务管理平台,集成基础服务,中间件服务,监控告警服务等。
IT-CLOUD-ACTIVITI6 :Activiti教程源码。博文在本CSDN Activiti系列中。
IT-CLOUD-ELASTICSEARCH :elasticsearch教程源码。博文在本CSDN elasticsearch系列中。
IT-CLOUD-KAFKA :spring整合kafka教程源码。博文在本CSDN kafka系列中。
IT-CLOUD-KAFKA-CLIENT :kafka client教程源码。博文在本CSDN kafka系列中。
IT-CLOUD-ZOOKEEPER :zookeeper客户端Curator事例。博文在本CSDN zookeeper系列中。开源项目,持续更新中,喜欢请 Star~