一、vmware设置虚拟机centeros的ip
1、设置vm属性
先设置NAT模式
点击菜单栏编辑
记录子网网关地址
2、修改linux配置
【1】修改linux ip地址
vi /etc/sysconfig/network-scripts/ifcfg-ens* (*根据实际情况不同,本文为ens33)
vi /etc/sysconfig/network-scripts/ifcfg-ens33
BOOTPROTO=static
IPADDR=192.168.220.10
NETMASK=255.255.255.0
GATEWAY=192.168.220.2
ONBOOT=yes
ONBOOT:是指系统启动时是否激活网卡,默认为no,设置为yes,表示开机启动时激活网卡。
BOOTPROTO
:网络分配方式,静态。(一定记得修改为Static,否则无法连通网络)
IPPADDR
:手动指定ip地址。
NETMASK
:子网掩码。
GATEWAY
:网关ip。
【2】设置主机名
vim /etc/hostname
【3】重启
重启网卡,使用service network restart命令重启网卡。
重启虚拟机 reboot
3、更改本地配置
4、验证
通过xshell ping下linuxip
连接成功
连接外网成功
二、安装zookeeper
1、下载zookeeper
wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.6.1/apache-zookeeper-3.6.1-bin.tar.gz
2、解压到opt目录下
tar -zxvf apache-zookeeper-3.6.1-bin.tar.gz -C /opt
3、给zk版本目录建立软连接
ln -s apache-zookeeper-3.6.1-bin/ zookeeper
4、设置zookeeper的path路径
vi /etc/profile
加入环境变量
export ZK_HOME=/opt/zookeeper
export PATH=PATH
应用path
source /etc/profile
5、修改配置文件
cd /opt/zookeeper
复制一份配置文件
cp zoo_sample.cfg zoo.cfg
修改配置
a 修改数据地址
dataDir=/usr/data/zookeeper
server.1=192.168.220.10:2888:3888
server.2=192.168.220.11:2888:3888
server.3=192.168.220.12:2888:3888
server.4=192.168.220.12:2888:3888:observer
6、启动zk
zkServer.sh start
zkServer.sh status
发现集群模式并未真正运行
7、复制虚拟机
通过克隆的方式把集群中的zk2、zk3、zk4克隆出来
然后依次修改克隆出来的主机ip、hostname、conf
8、问题
a myid找不到
在dataDir路径/usr/data/zookeeper 下创建myid文件并设置myid
b java.net.NoRouteToHostException: No route to host (Host unreachable)
需要关闭防火墙
systemctl stop firewalld
9、启动
命令
zkServer.sh start
zkServer.sh restart
zkServer.sh status
zkServer.sh stop
结果
zkOS1
zkOS2
zkOS3
zkOS4
三、zk故障恢复
1、正常状态数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jdxHVbxu-1594306317993)(asserts/1590302120084.png)]
2、模拟leader宕机
leader是server2
zkServer3成为新的leader
重新启动zkSever2成为follower
四、zookeeper java客户端
1、zkClient
在使用ZooKeeper的Java客户端时,经常需要处理几个问题:重复注册watcher、session失效重连、异常处理。
要解决上述的几个问题,可以自己解决,也可以采用第三方的java客户端来完成。这里就介绍一种常用的客户端zkclient,目前已经运用到了很多项目中,知名的有Dubbo、Kafka、Helix。
【1】依赖
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
【2】 增删改查
public class ZkClientBase {
/** zookeeper地址 */
static final String CONNECT_ADDR = "192.168.1.31:2181,192.168.1.32:2181,192.168.1.33:2181";
/** session超时时间 */
static final int SESSION_OUTTIME = 10000;//ms
public static void main(String[] args) throws Exception {
ZkClient zkc = new ZkClient(new ZkConnection(CONNECT_ADDR), SESSION_OUTTIME);
//1. create and delete方法
zkc.createEphemeral("/temp");
zkc.createPersistent("/super/c1", true);
Thread.sleep(10000);
zkc.delete("/temp");
zkc.deleteRecursive("/super");
//2. 设置path和data 并且读取子节点和每个节点的内容
zkc.createPersistent("/super", "1234");
zkc.createPersistent("/super/c1", "c1内容");
zkc.createPersistent("/super/c2", "c2内容");
List<String> list = zkc.getChildren("/super");
for(String p : list){
System.out.println(p);
String rp = "/super/" + p;
String data = zkc.readData(rp);
System.out.println("节点为:" + rp + ",内容为: " + data);
}
//3. 更新和判断节点是否存在
zkc.writeData("/super/c1", "新内容");
System.out.println(zkc.readData("/super/c1").toString());
System.out.println(zkc.exists("/super/c1"));
// 4.递归删除/super内容
zkc.deleteRecursive("/super");
}
}
【3】注册节点监听器
(1) 订阅节点数据变化
监听节点数据变化、节点删除
public class ZkClientWatcher2 {
/** zookeeper地址 */
static final String CONNECT_ADDR = "192.168.1.31:2181,192.168.1.32:2181,192.168.1.33:2181";
/** session超时时间 */
static final int SESSION_OUTTIME = 10000;//ms
public static void main(String[] args) throws Exception {
ZkClient zkc = new ZkClient(new ZkConnection(CONNECT_ADDR), SESSION_OUTTIME);
zkc.createPersistent("/super", "1234");
//对父节点添加监听子节点变化。
zkc.subscribeDataChanges("/super", new IZkDataListener() {
@Override
public void handleDataDeleted(String path) throws Exception {
System.out.println("删除的节点为:" + path);
}
@Override
public void handleDataChange(String path, Object data) throws Exception {
System.out.println("变更的节点为:" + path + ", 变更内容为:" + data);
}
});
Thread.sleep(3000);
zkc.writeData("/super", "456", -1);
Thread.sleep(1000);
zkc.delete("/super");
Thread.sleep(Integer.MAX_VALUE);
}
}
(2)订阅子节点的变化
监听如下三种变化
- 新增子节点
- 减少子节点
- 删除节点
注意: 不监听节点内容的变化
public class ZkClientWatcher1 {
/** zookeeper地址 */
static final String CONNECT_ADDR = "192.168.1.31:2181,192.168.1.32:2181,192.168.1.33:2181";
/** session超时时间 */
static final int SESSION_OUTTIME = 10000;//ms
public static void main(String[] args) throws Exception {
ZkClient zkc = new ZkClient(new ZkConnection(CONNECT_ADDR), SESSION_OUTTIME);
//对父节点添加监听子节点变化。
zkc.subscribeChildChanges("/super", new IZkChildListener() {
@Override
public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
System.out.println("parentPath: " + parentPath);
System.out.println("currentChilds: " + currentChilds);
}
});
Thread.sleep(3000);
zkc.createPersistent("/super");
Thread.sleep(1000);
zkc.createPersistent("/super" + "/" + "c1", "c1内容");
Thread.sleep(1000);
zkc.createPersistent("/super" + "/" + "c2", "c2内容");
Thread.sleep(1000);
zkc.delete("/super/c2");
Thread.sleep(1000);
zkc.deleteRecursive("/super");
Thread.sleep(Integer.MAX_VALUE);
}
}
(3)订阅zk状态变化
public class ZkStateWatcher {
static final String CONNECT_ADDR = "172.21.121.53:2181,172.21.121.54:2181,172.21.121.55:2181";
static final int CONNECTION_OUTTIME = 5000;
public static void main(String[] args) throws InterruptedException{
ZkClient zkc = new ZkClient(new ZkConnection(CONNECT_ADDR),CONNECTION_OUTTIME);
zkc.subscribeStateChanges(new IZkStateListener() {
@Override
public void handleStateChanged(KeeperState state) throws Exception {
if(state==KeeperState.SyncConnected){
//当我重新启动后start,监听触发
System.out.println("连接成功");
}else if(state==KeeperState.Disconnected){
System.out.println("连接断开");//当我在服务端将zk服务stop时,监听触发
}else
System.out.println("其他状态"+state);
}
@Override
public void handleNewSession() throws Exception {
System.out.println("---->重建session");
}
});
// zkc.close();
Thread.sleep(Integer.MAX_VALUE);
}
}
2、curator
解决Watch注册一次就会失效的问题
支持直接创建多级结点
提供的 API 更加简单易用
提供更多解决方案并且实现简单,例如:分布式锁
提供常用的ZooKeeper工具类
编程风格更舒服
【1】依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
<type>pom</type>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-framework -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
</dependencies>
【2】增删改查
创建连接
private static void createZkCuratorConnection(String ipPort) {
curatorFramework=CuratorFrameworkFactory
.builder() // 使用工厂类来建造客户端的实例对象
.connectString(ipPort) // 配置zk服务器IP port
.sessionTimeoutMs(4000)// 设定会话时间
.retryPolicy(new ExponentialBackoffRetry(1000,3))//设置及重连策略
.namespace("curator")//方便管理的命名空间,其实就是一级目录
.build();//建立管道
curatorFramework.start();//开启curator
}
连接关闭
private static void closeZkCuratorConnection() {
curatorFramework.close();
}
创建节点
private static void createZnode(String path, String value) throws Exception {
curatorFramework
.create()//创建Znode
.creatingParentsIfNeeded()//如果是多级结点,这里声明如果需要,自动创建父节点
.withMode(CreateMode.PERSISTENT)//声明结点类型
.forPath(path,value.getBytes());//声明结点路径和值
}
删除节点
private static void deleteZnode(String path) throws Exception {
curatorFramework
.delete()
.deletingChildrenIfNeeded()//如果有子节点,会先自动删除子节点再删除本结点
.forPath(path);
}
查询节点值
private static void getZnodeData(String path) throws Exception {
byte[] dataBytes = curatorFramework.getData().forPath(path);
System.out.println("结点值为:" +new String(dataBytes));
}
设置新值
private static void setValue(String path,String value) throws Exception {
Stat stat = curatorFramework.checkExists().forPath(path);
if (stat==null){
System.out.println("Znode does not exists");
}else {
curatorFramework
.setData()
.withVersion(stat.getVersion())
.forPath(path,value.getBytes());
}
}
【3】注册节点监听器
Cutator提供了三种完善而又灵活的监听机制
- PathchildCache ~监听一个节点下子节点的创建、删除、更新
- NodeCache ~监听一个节点的更新和创建事件(不包括删除)
- TreeCache ~综合PatchChildCache和INodeCache的特性
(1)添加事件监听
private static void addWatcherWithNodeCache(String path) throws Exception {
final NodeCache nodeCache=new NodeCache(curatorFramework,path,false);
NodeCacheListener nodeCacheListener=new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
System.out.println("事件路径:"+nodeCache.getCurrentData().getPath()+"发生数据变化,新数据为"+new String(nodeCache.getCurrentData().getData()));
}
};
nodeCache.getListenable().addListener(nodeCacheListener);
nodeCache.start();
}
(2)监听孩子节点变化
private static void addWatcherWithChildrenCache(String path) throws Exception {
final PathChildrenCache childrenCache=new PathChildrenCache(curatorFramework,path,true);//缓存数据
PathChildrenCacheListener pathChildrenCacheListener=new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
System.out.println("事件路径:"+pathChildrenCacheEvent.getData().getPath()+"事件类型"+pathChildrenCacheEvent.getType());
}
};
childrenCache.getListenable().addListener(pathChildrenCacheListener);
childrenCache.start(PathChildrenCache.StartMode.NORMAL);
}
(3)treeNode监听所有变化
private static void addWatcherWithTreeCache(String path) throws Exception {
TreeCache treeCache=new TreeCache(curatorFramework,path);
TreeCacheListener treeCacheListener=new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
System.out.println("事件路径:"+treeCacheEvent.getData().getPath()+"事件类型"+treeCacheEvent.getType()+"结点值为"+new String(treeCacheEvent.getData().getData()));
}
};
treeCache.getListenable().addListener(treeCacheListener);
treeCache.start();
}
childrenCache.getListenable().addListener(pathChildrenCacheListener);
childrenCache.start(PathChildrenCache.StartMode.NORMAL);
}