大家好,又见面了,我是你们的朋友全栈君。
客户端首先将Watcher注册到服务器,同时将Watch对象保存到客户端的Watch管理器中。当Zookeeper服务器监听到的数据发生变化时,服务器会通知客户端,接着客户端的Watch管理器会触发相关的Watcher来回调响应处理逻辑,从而完成整体的数据发布/订阅流程。
javaAPI
Java watch案例
public class WatcherDemo implements Watcher {
static ZooKeeper zooKeeper;
static {
try {
zooKeeper = new ZooKeeper("192.168.3.39:2181", 4000,new WatcherDemo());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void process(WatchedEvent event) {
System.out.println("eventType:"+event.getType());
if(event.getType()==Event.EventType.NodeDataChanged){
try {
zooKeeper.exists(event.getPath(),true);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
String path="/watcher";
if(zooKeeper.exists(path,false)==null) {
zooKeeper.create("/watcher", "0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
Thread.sleep(1000);
System.out.println("-----------");
//true表示使用zookeeper实例中配置的watcher
Stat stat=zooKeeper.exists(path,true);
System.in.read();
}
}
运行完程序,控制台显示
:此时启动 zookeeper 命令行终端,查看并且删除 watcher 节点:
IDE 控制台输出,触发了节点删除事件:
使用zookeeper作为配置中心,通常情况下java应用程序会依赖很多配置文件,建议将配置信息配置在zookeeper中,将zookeeper作为服务器的配置中心,当配置发生变换之后,可以用zookeeperz中的watch机制捕获到发生变化的事件,以便客户端获得配置信息。
设计思路
package com.cc.duoxiancheng;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.util.concurrent.CountDownLatch;
public class MyConfigCenter implements Watcher {
// zookeeper地址
String IP = "192.168.60.130:2181";
// zookeeper是异步连接的,所以要使用countDownLatch 当响应返回的时候才继续改线程
CountDownLatch countDownLatch = new CountDownLatch(1);
static ZooKeeper zooKeeper;
// 配置信息
private String url;
private String username;
private String password;
@Override
public void process(WatchedEvent event) {
try {
if(event.getType() == Event.EventType.None) {
if(event.getState() == Event.KeeperState.SyncConnected){
System.out.println("连接成功");
countDownLatch.countDown();
}
else if(event.getState() == Event.KeeperState.Disconnected){
System.out.println("连接断开");
}
else if(event.getState() == Event.KeeperState.Expired){
System.out.println("连接超时");
}
else if(event.getState() == Event.KeeperState.AuthFailed){
System.out.println("认证失败");
}
}else if(event.getType() == Event.EventType.NodeDataChanged){
//当有数据变华的时候,重新从配置信息中读取信息
initValue();
}
}catch (Exception e){
e.printStackTrace();
}
}
private MyConfigCenter(){
initValue();
}
public void initValue()
{
try {
zooKeeper = new ZooKeeper(IP,50000,this);
countDownLatch.await();
//注册配置信息的watch道主watch中
this.url = new String(zooKeeper.getData("/config/url",true,null));
this.username = new String(zooKeeper.getData("/config/username",true,null));
this.password = new String(zooKeeper.getData("/config/password",true,null));
}catch (Exception e){
e.printStackTrace();
}
}
}
在过去的单库单表系统中,通常可以使用数据库的AUTO_INCREMENT属性来自动为每条记录生成一个唯一的ID,d但是分库分表后,就无法在依靠数据库的auto_increment属性来唯一表示一条记录了。此时我们就可以用zookeeper在分布式环境下生成全局唯一id
设计思路
zookeeper中创建分布式唯一ID
public String getUniqueId(){
String path = "";
try{
//生成zookeeper有序临时节点
path = zookeeper.create(path,new byte[0],Ids.OPEN_ACL_UNSAFE,CREATEMode.EPHEMERAL_SEQUENTIAL);
}catch(Exception e){
e.printStackTrace();
}
//
return path.subString(...);
}
分布式锁有多种实现方式,比如数据库,Redis都可以实现分布式锁,作为分布式协同工具zookeeper,当然也有着标准的实现方式。 设计思路
java案例
private void createLock() throws Exception{
//判断节点是否存在
Stat stat = zooKeeper.exists(LOCK_ROOT_PATH,false);
if(stat == null){
zooKeeper.create(LOCK_ROOT_PATH,new byte[0],ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
//创建临时有序节点
lockPath = zooKeeper.create(LOCK_ROOT_PATH + "/" + LOCK_NODE_NAME,new byte[0],ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
Watcher watch = new Watcher(){
public void process(){
if(event.getType == Event,EventType.NodeDeleted){
synchronized(this){
notifyAll();
}
}
}
}
private void attemptLock() throws Exception{
//获取Locks节点下所有孩子节点
List<String>list = zooKeeper.getChildren(LOCK_ROOT_PATH,false);
//对节点进行排序
Collection.sort(list);
int index = list.indexOf(lockPath.subString(LOCK_ROOT_PATH.length() + 1));
if(index == 0){
System.out.println("获取锁成功");
}else {
//上一个节点路径
String path = list.get(index-1);
zooKeeper.exists(LOCK_ROOT_PATH + "/" + path,wathcer);
if(stat == nukk){
attemptLock();
}else{
synchronized(watcher){
watcher.wait();
}
attemptLock();
}
}
}
public void releaseLock() throws Exception{
zooKeeper.delete(this.lockPath,-1);
zooKeeper.close();
System,out.println("锁已经释放" + this.lockPath);
}
Ubuntu进行虚拟集群搭建。模拟共有3台服务器,端口分别为2181,2182,2183
cp -r zookeeper-3.4.10 zookeeper2181
cp -r zookeeper-3.4.10 zookeeper2182
cp -r zookeeper-3.4.10 zookeeper2183
# 指定ip
clientPortAddress=192.168.0.120
# 配置集群
server.服务器编号=ip地址:Zookeeper服务器之间的通信端口:Leader选举的端口
server.1=192.168.0.120:2287:3387
server.2=192.168.0.120:2288:3388
server.3=192.168.0.120:2289:3389
# zookeeper2181对应的服务器编号是1
# /home/zookeeper/zookeeper2181/data目录下
echo "1" > myid
# 启动
zkServer.sh start
# 状态
zkServer.sh status
# 登录
zkCli.sh -server 192.168.60.130:2181
zab协议的全程是Zookeeper Atomic Broadcast,zookeeper是通过zab协议来保证分布式事务的最终一致性 基于zab协议,zookeeper集群中的角色主要有以下三类,如下表所示:
zab原子广播模式工作原理,通过类似两阶段提交协议的方法解决数据一致性:
服务器状态
服务器启动时的leader选举 每个节点启动的时候状态都是LOOKING,处于观望状态,接下来就开始进行选主流程
若进行Leader选举,则至少需要两台机器,这里选取3台机器组成的服务器集群为例。在集群初始化阶段,当有一台服务器Server1启动时,其单独无法进行和完成Leader选举,当第二台服务器Server2启动时,此时两台机器可以相互通信,每台机器都试图找到Leader,于是进入Leader选举过程。选举过程如下
对于Server1而言,它的投票是(1, 0),接收Server2的投票为(2, 0),首先会比较两者的ZXID,均为0,再比较myid,此时Server2的myid最大,于是更新自己的投票为(2, 0),然后重新投票,对于Server2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
运行时的leader选举 当集群中的leader服务器出现宕机或者不可用的情况时,那么整个集群将无法对外提供服务,而是进入新一轮的Leader选举,服务器运行期间的Leader选举和启动时期的Leader选举基本过程是一致的。
Observer角色特点:
peerType=observer
并在所有server的配置文件中,配置成observer模式的server的呐喊配置追加:observer,例如:
server.3=192.168.60.130:2289:3389:observer
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/168796.html原文链接:https://javaforall.cn