Zk概述
Zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper就将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应。
zookeeper=文件系统+通知机制
特点
-
Zookeeper:一个领导者,多个跟随着组成的集群。
-
集群中只要有半数以上节点存活,Zookeeper集群就能正常服务。所以Zookeeper适合安装奇数台服务器
-
全局数据一致:每个Server保存一份相同的数据副本,Client无论连接到哪个Server,数据都是一致的。
-
更新请求顺序执行,来自同一个Client的更新请求按其发送顺序依次执行
-
数据更新原子性次数据更新要么成功,要么失败
-
实时性,在一定时间范围内,Client能读到最新的数据
解决问题
- 统一命名服务
- 统一配置管理
- 同意集群管理
- 服务器的动态上下线
选举机制
-
服务器1启动,发起一次选举此时服务器1票数一票,不够半数以上(3票),选举无法完成,服务器1状态保LOOKING;
-
服务器2启动,再发起一次选举。服务器1和2分别投自己一票并交换选票信息:此时服务器1发现服务器2的myid比自己目前投票推举的(服务器1)大,更改选票为推举服务器2。此时服务器1票数0票,服务器2票数2票,没有半数以上结果,选举无法完成,服务器1,2状态保持LOOKING
-
服务器3启动,发起一次选举。此时服务器1和都会更改选票为服务器3。此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数,服务器3当选Leader。服务器1,2更改状态FOLLOWING,服务器3更改状态为LEADING:
-
服务器4启动,发起一次选举。此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为1票。此时服务器4服从多数,更改选票信息为服务器3,并更改状态为FOLLOWING;
非第一次启动:
public class zkClient {
//一开始sessionTimeout=2000 怎么实验都失败设置成
private String connectString="10.5.150.38:2181,10.5.82.210:2181,10.5.150.53:2181";
private int sessionTimeout=80000;
ZooKeeper zkClient;
@Before
public void init() throws IOException {
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
//监听器
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("----------");
List<String> children = null;
try {
children = zkClient.getChildren("/", true);
for (String child:children) {
System.out.println(child);
}
System.out.println("----------");
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
//TODO 创建节点
@Test
public void create() throws KeeperException,InterruptedException{
String nodeCreated = zkClient.create("/atguigu", "ss.avi".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
//TODO 监听节点变化
@Test
public void getChildren() throws InterruptedException, KeeperException {
List<String> children = zkClient.getChildren("/", true);
for (String child:children) {
System.out.println(child);
}
//延时
Thread.sleep(Long.MAX_VALUE);
}
//TODO 判断节点是否存在
@Test
public void exist() throws InterruptedException, KeeperException {
Stat stat = zkClient.exists("/atguigu", false);
System.out.println(stat==null?"not exist":"exist");
}
}
服务器动态上下线
服务端:
public class DistributeServer {
private String connectString="10.5.150.38:2181,10.5.82.210:2181,10.5.150.53:2181";
private int sessionTimeout=60000;
private ZooKeeper zk;
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
DistributeServer server = new DistributeServer();
//1. 获取zk连接
server.getConnect();
//2.注册服务器到zk集群
server.regist(args[0]);
//3.启动业务逻辑(睡觉)
server.busniess();
}
private void busniess() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
private void regist(String hostname) throws InterruptedException, KeeperException {
String s = zk.create("/servers/"+hostname, hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname+"is online");
}
private void getConnect() throws IOException {
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
}
});
}
}
客户端:
public class DistributeClient {
private String connectString="10.5.150.38:2181,10.5.82.210:2181,10.5.150.53:2181";
private int sessionTimeout=80000;
private ZooKeeper zk;
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
DistributeClient client = new DistributeClient();
//1.获取zk连接
client.getConnect();
//2.监听servers下面子节点的增加删除
client.getServerList();
//3.业务逻辑(睡觉)
client.business();
}
private void business() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
private void getServerList() throws InterruptedException, KeeperException {
List<String> children = zk.getChildren("/servers", true);
ArrayList<Object> servers = new ArrayList<>();
for (String child : children) {
byte[] data = zk.getData("/servers/" + child, false, null);
servers.add(new String(data));
}
System.out.println(servers);
}
private void getConnect() throws IOException {
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
try {
getServerList();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
});
}
}
分布式锁案例
public class CuratorLockTest {
public static void main(String[] args) {
//创建分布式锁1
InterProcessMutex lock1 = new InterProcessMutex(getCuratorFramework(), "/locks");
//创建分布式锁2
InterProcessMutex lock2 = new InterProcessMutex(getCuratorFramework(), "/locks");
new Thread(new Runnable() {
@Override
public void run() {
try {
lock1.acquire();
System.out.println("线程1 获取到锁");
lock1.acquire();
System.out.println("线程1 再次获取到锁");
Thread.sleep(5*1000);
lock1.release();
System.out.println("线程1 释放锁");
System.out.println("线程1 再次释放了锁");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock2.acquire();
System.out.println("线程2 获取到锁");
lock1.acquire();
System.out.println("线程2 再次获取到锁");
Thread.sleep(5*1000);
lock1.release();
System.out.println("线程2 释放锁");
System.out.println("线程2 再次释放了锁");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
private static CuratorFramework getCuratorFramework() {
//创建客户端
ExponentialBackoffRetry policy = new ExponentialBackoffRetry(3000, 3);
CuratorFramework client = CuratorFrameworkFactory.builder().connectString("10.5.150.38:2181,10.5.82.210:2181,10.5.150.53:2181")
.connectionTimeoutMs(40000)
.sessionTimeoutMs(2000)
.retryPolicy(policy).build();
//创建客户端
client.start();
System.out.println("zookeeper 启动成功");
return client;
}
}