这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战;
zookeeper的一些知识点
zookeeper的安装
1.准备工作:
#! 准备3个节点,要求配置好主机名称,服务器之间系统时间保持─致
#!注意/etc/hostname和/etc/hosts配置主机名称(在这个里我准备hkh221,hkh222,hkh223三节点)
#!特别主意以下操作3个节点要同时进行操作哦!
并
#!注意关闭防火墙
启动防火墙 systemctl start firewalld
关闭防火墙 systemctl stop firewalld
重启防火墙 systemctl restart firewalld
查看防火墙状态 systemctl status firewalld
开机禁用防火墙 systemctl disable firewalld
#!本地访问: ping
2.上传zk到三台服务器节点
#!注意我这里解压到/usr/local下
2.1进行解压:tar zookeeper-3.4.6.tar.gz
2.2重命名:mv zookeeper-3.4.6 zookeeper
2.3修改环境变量:vim /etc/profile
#!这里要添加zookeeper的全局变量
export zOOKEEPER_HOME=/usr/local/zookeeper
export PATH=.:$ZOOKEEPER_HOME/bin
#2.4刷新环境变量:
source /etc/profile
#!2.5到zookeeper下修改配置文件:
#! 2.5.1首先到指定目录:
cd /usr/local/zookeeper/conf
#! 2.5.2然后复制zoo_sample.cfg文件,复制后为zoo.cfg:
mv zoo_sample.cfg zoo.cfg
#! 2.5.3 然后修改两处地方,最后保存退出:wq
#! (1)修改数据的dir
dataDir=/usr/local/zookeeper/data
#! (2)修改集群地址
server.0=hkh221:2888:3888
server.1=hkh222:2888:3888
server.2=hkh223:2888:3888
并且2.5.4 增加服务器标识配置,需要2步骤,第一是创建文件夹和文件,第二是添加配置内容:
#!(1)创建文件夹:
mkdir /usr/local/zookeeper-3.4.6/data
#!(2)创建文件myid 路径应该创建在/usr/local/zookeeper-3.4.6/data下面,如下:
vim /usr/local/zookeeper-3.4.6/data/ myid
注意这里每一台服务器的myid文件内容不同,分别修改里面的值为0,1,2;与我们之前的zoo.cfg配置文件里:
server.0,server.1,server.2 顺序相对应,然后保存退出;
#!2.7到此为止,Zookeeper集群环境大功告成!启动zookeeper命令
启动路径:/usr/local/zookeeper/bin(也可在任意目录,因为配置了环境变量)
执行命令: zkServer.sh start
(注意这里3台机器都要进行启动,启动之后可以查看状态)
#!查看状态:
zkServer.sh status
(在三个节点上检验zk的mode,会看到一个1eader和俩个follower)
zkCli.sh 进入zookeeper客户端
#!根据提示命令进行操作:
查找: ls /ls /zookeeper
#!创建并赋值:
create /jacquesh zookeeper
获取:get /jacquesh
设值:set /jacquesh zookeeper1314
PS1:任意节点都可以看到zookeeper集群的数据─致性
PS2:创建节点有俩种类型:短暂(ephemeral)持久(persistent)
#! 设置zk开机启动
cd /etc/rc.d/init.d/
touch zookeeper
chmod 777 zookeeper
vim zookeeper
开机启动zookeeper脚本:
#!/bin/bash
#chkconfig:2345 20 90
#description :zookeeper
#processname :zookeeper
export JAVA_HOME=/usr/local/jdk1.8
export PATH=$JAVA_HOME/bin:$PATH
case $1 in
start) /usr/local/zookeeper-3.4.6/bin/zkServer.sh start;;
stop) /usr/local/zookeeper-3.4.6/bin/zkServer.sh stop;;
status) /usr/local/zookeeper-3.4.6/bin/zkServer.sh status;;
restart) /usr/local/zookeeper-3.4.6/bin/zkServer.sh restart;;
*) echo "require start|stop|status|restart";;
esac
#!开机启动配置:
chkconfig zookeeper on
#!验证:
chkconfig --add zookeeper
chkconfig --list zookeeper
#! 启动/停止zookeeper服务
service zookeeper start/stop
#! 把zookeeper添加到开机启动里面
chkconfig --add zookeeper
#!查看添加的zookeeper是否在里面开机任务中
chkconfig --list zookeeper
代码
pom文件
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.9</version>
</dependency>
ZKUtils
public class ZKUtils {
//zookeeper实例
private ZooKeeper zk;
//分布式业务锁业务目录
private String rootPath = "/lock";
//zookeeper地址
private String address = "193.112.8.126:2181";
//zookeeper实例化的闸门 防止未实例就调用
private CountDownLatch createLatch = new CountDownLatch(1);
//分布式线程锁
private CountDownLatch threadLatch = new CountDownLatch(1);
//watch
private DefaultWatch watch;
private Integer timeout = 10000;
public static class Builder {
/**
* 使用构造者模式对主要数据对外暴露配置
*/
private Integer timeout;
private String address;
private String rootPath;
public Builder setTimeout(Integer timeout) {
this.timeout = timeout;
return this;
}
public Builder setAddress(String address) {
this.address = address;
return this;
}
public Builder setRootPath(String rootPath) {
this.rootPath = rootPath;
return this;
}
public ZKUtils build() throws IOException, InterruptedException, KeeperException {
ZKUtils zkUtils = new ZKUtils();
if (address == null) address = zkUtils.address;
if (timeout == null) timeout = zkUtils.timeout;
if (rootPath == null) rootPath = zkUtils.rootPath;
zkUtils.zk = new ZooKeeper(address, timeout,
/**
* 加这段是为了防止报错 没啥影响也乐意不加-。-。
*/
event -> {
System.out.println(event.toString());
}
);
/**
* 初始化DefaultWatch
*/
zkUtils.watch = new DefaultWatch(zkUtils.createLatch, zkUtils.threadLatch, zkUtils.zk, rootPath);
/**
* 预先判断业务主节点是否存在 不存在创建
*/
Stat stat = zkUtils.zk.exists(rootPath, zkUtils.watch);
if (stat == null) {
zkUtils.zk.create(rootPath,
rootPath.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
/**
* 在创建指令发送后对进程加锁 防止node节点未创建就调用zookeeper的其他方法
*/
zkUtils.createLatch.await();
}
return zkUtils;
}
}
public static Builder builder() {
/**
* 返回构建者实例
*/
return new Builder();
}
/**
* 关闭方法
*/
public void closed() {
if (zk != null) {
try {
zk.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* @link DefaultWatch
* 执行分布式锁操作
* 实现过程坑看
* @see DefaultWatch#tryLock
*/
public void tryLock() {
watch.tryLock();
}
public void unLock() {
watch.unLock();
}
}
DefaultWatch
public class DefaultWatch implements Watcher, AsyncCallback.ChildrenCallback, AsyncCallback.Create2Callback {
CountDownLatch createLatch;
ZooKeeper zooKeeper;
CountDownLatch threadLatch;
String rootPath;
private String lockName;
public DefaultWatch(CountDownLatch createLatch,CountDownLatch threadLatch,ZooKeeper zooKeeper,String rootPath) {
this.threadLatch=threadLatch;
this.createLatch = createLatch;
this.zooKeeper=zooKeeper;
this.rootPath=rootPath;
}
public void tryLock() {
//重入
try {
/**
* 加锁的操作就是在zookeeper创建这个线程的node节点
* 使用CreateMode.EPHEMERAL_SEQUENTIAL 会有序号
*/
zooKeeper.create(rootPath+"/lock", Thread.currentThread().getName().getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL,
this, Thread.currentThread().getName());
/**
* 接下来就直接锁住线程就ok
* 等待 zookeeper 回调唤醒线程就ok
* 具体回调可看
* @see DefaultWatch#processResult(int, String, Object, String, Stat)
*/
threadLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void process(WatchedEvent event) {
switch (event.getType()){
case None :
System.out.println("None");
break;
case NodeCreated:
if (event.getPath().equals(rootPath)){
System.out.println(event.getPath()+"节点创建");
createLatch.countDown();
System.out.println("放行");
}
break;
case NodeDeleted:
System.out.println("NodeDeleted");
break;
case NodeDataChanged:
System.out.println("NodeDataChanged");
break;
case NodeChildrenChanged:
System.out.println("NodeChildrenChanged");
zooKeeper.getChildren(rootPath, this,this,"");
break;
case DataWatchRemoved:
System.out.println("DataWatchRemoved");
break;
case ChildWatchRemoved:
System.out.println("ChildWatchRemoved");
break;
}
}
/**
*请求所有子节点回调的功能就是对所有的node节点进行排序
* 选择最小的那一个进行锁的放开
*
*/
@Override
public void processResult(int rc, String path, Object ctx, List<String> children) {
if(children!=null&&children.size()>0){
Collections.sort(children);
if(lockName.equals(rootPath+"/"+children.get(0))){
threadLatch.countDown();
}
}else{
/**
* 如果业务根节点没有子节点 就只能说明所有任务都完成了
*/
System.out.println("所有任务执行完成");
}
}
/**
*创建节点后的回调
*/
@Override
public void processResult(int rc, String path, Object ctx, String name, Stat stat) {
//每个线程启动后创建锁,然后get锁目录的所有孩子,不注册watch在锁目录
System.out.println(ctx.toString()+" create path: "+ name);
/**
* 创建节点后将创建的节点名称赋值给锁名称
* 接下来获取业务根目录的所有子节点
* 只是将请求过去 还是得等待回调
* 请求查看子节点的回调可看
* @see DefaultWatch#processResult(int, String, Object, List)
*/
lockName = name;
zooKeeper.getChildren(rootPath, this, this, ctx );
}
/**
* 释放锁操作的 就是将这个子节点给删了
* 删除的时候也会进行回调
* 回调的时候所有线程都会去getChildren 判断自己是否为获得锁的线程
* 回调可以看
* @see DefaultWatch#process(WatchedEvent)
*/
public void unLock() {
try {
zooKeeper.delete(lockName,-1);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
}
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}