五、zkClient操作Zookeeper
使用zookeeper遇到问题:
- 重复注册watcher
- session失效重连
- 异常处理(删除节点不能有子节点,新增节点必须有父节点等)
zkclient是Github上一个开源的Zookeeper客户端,在Zookeeper原生 API接口之上进行了包装,是一个更加易用的Zookeeper客户端。同时Zkclient在内部实现了诸如Session超时重连,Watcher反复注册等功能,从而提高开发效率。
添加依赖
<dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.10</version> </dependency>
创建会话
//1.创建会话 ZkClient zkClient = new ZkClient("192.168.66.100:2181,192.168.66.101:2181,192.168.66.102:2181");
创建节点
//3.创建节点 zkClient.create("/zkTest/zkTest3","3", CreateMode.PERSISTENT);
获取/zkTest子节点
//2.获取/zkTest子节点 List<String> children = zkClient.getChildren("/zkTest"); children.forEach(System.out::println);
修改节点数据
zkClient.writeData("/zkTest/zkTest3","4");
获取节点数据
//5.获取节点数据 String data = zkClient.readData("/zkTest/zkTest3");
删除节点
//6.删除节点 zkClient.delete("/zkTest/zkTest3");
注册节点监听事件
//7.注册节点监听事件 zkClient.subscribeChildChanges("/zkTest",new IZkChildListener(){ @Override public void handleChildChange(String s, List<String> list) throws Exception { System.out.println("节点改变了"); list.forEach(System.out::println); } });
注册数据监听事件
//8.注册数据监听事件 zkClient.subscribeDataChanges("/zkTest", new IZkDataListener() { @Override public void handleDataChange(String s, Object o) throws Exception { System.out.println("数据改变了"); } @Override public void handleDataDeleted(String s) throws Exception { System.out.println("数据删除了"); } });
六、ApacheCurator操作Zookeeper
Curator是 Netflix公司开源的一套ZooKeeper客户端框架。和ZkClient一样,Curator解决了很多ZooKeeper客户端非常底层的细节开发工作,包括连接重连、反复注册Watcher和 NodeExistsException异常等,目前已经成为了Apache的顶级项目,是全世界范围内使用最广泛的ZooKeeper客户端之一。
Curator包
- curator-framework:对zookeeper的底层api的一些封装。
- curator-client:提供一些客户端的操作,例如重试策略等。
- curator-recipes:封装了一些高级特性,如:Cache事件监听、选举、分布式锁、分布式计数器、分布式Barrier等。
添加Maven依赖
<dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>4.2.0</version> </dependency>
创建会话
//1.创建会话 String connStr = "192.168.66.100:2181,192.168.66.101:2181,192.168.66.102:2181"; CuratorFramework cur= CuratorFrameworkFactory.builder() .connectString(connStr) .connectionTimeoutMs(5000) .retryPolicy(new ExponentialBackoffRetry(1000,3)) //断开重试机制 .build(); //2.连接 cur.start();连接
创建节点
//3.创建节点 cur.create().withMode(CreateMode.PERSISTENT).forPath("/zkTest/zkTest4","4".getBytes());
获取数据
//4.获取数据 byte[] bytes = cur.getData().forPath("/zkTest/zkTest4"); System.out.println(new String(bytes));
删除一个节点
//5.删除节点 cur.delete().forPath("/zkTest/zkTest4");
注意:
此方法只能删除叶子节点,否则会抛出异常。
删除一个节点,并且递归删除其所有的子节点
cur.delete().deletingChildrenIfNeeded().forPath("/zkTest");
更新一个节点的数据内容
client.setData().forPath("path","data".getBytes());
监听节点
//8.监听机制 NodeCache nodeCache = new NodeCache(cur, "/zkTest/zkTest3"); nodeCache.getListenable().addListener(()-> System.out.println("被修改")); nodeCache.start();
七、Zookeeper高级
7.1 四字命令
之前使用stat命令来验证ZooKeeper服务器是否启动成功,这里的stat命令就是ZooKeeper 中最为典型的命令之一。ZooKeeper中有很多类似的命令,它们的长度通常都是4个英文字母,因此我们称之为“四字命令”。
添加配置(前提)
vim zoo.cfg #开启四字命令 4lw.commands.whitelist=* #添加nc命令 yum install nc -y
conf
输出Zookeeper相关服务的详细配置信息,如客户端端口,数据存储路径、最大连接数、日志路径、数据同步端口、主节点推举端口、session超时时间等等。
echo conf| nc localhost 2181
注意:
注意,conf命令输出的配置信息仅仅是输出一些最基本的配置参数。另外,conf命令会根据当前的运行模式来决定输出的信息。如果是单机模式(standalone), 就不会输出诸如initLimit.syncLimit、electionAlg 和electionPort等集群相关的配置信息。
cons
cons 命令用于输出当前这台服务器上所有客户端连接的详细信息,包括每个客户端的客户端IP、会话ID和最后一次与服务器交互的操作类型等。
echo cons | nc localhost 2181
ruok
ruok命令用于输出当前ZooKeeper服务器是否正在运行。该命令的名字非常有趣,其谐音正好是“Are you ok”。执行该命令后,如果当前ZooKeeper服务器正在运行,那么返回“imok”, 否则没有任何响应输出。
echo ruok | nc localhost 2181
stat
stat命令用于获取ZooKeeper服务器的运行时状态信息,包括基本的ZooKeeper版本、打包信息、运行时角色、集群数据节点个数等信息,另外还会将当前服务器的客户端连接信息打印出来。
echo stat | nc localhost 2181
注意:
除了一些基本的状态信息外,stat命令还会输出一些服务器的统计信息,包括延迟情况、收到请求数和返回的响应数等。注意,所有这些统计数据都可以通过srst命令进行重置。
mntr
列出集群的关键性能数据,包括zk的版本、最大/平均/最小延迟数、数据包接收/发送量、连接数、zk角色(Leader/Follower)、node数量、watch数量、临时节点数。
echo mntr | nc localhost 2181
7.2 选举机制
核心选举原则
- Zookeeper集群中只有超过半数以上的服务器启动,集群才能正常工作;
- 在集群正常工作之前,myid小的服务器给myid大的服务器投票,直到集群正常工作,选出Leader;
- 半数机制;
选举机制流程
- 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking(选举状态)。
- 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
- 服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。
- 服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟。
- 服务器5启动,后面的逻辑同服务器4成为小弟。
选择机制中的概念
Serverid:服务器ID
比如有三台服务器,编号分别是1,2,3。
编号越大在选择算法中的权重越大。
Zxid:数据ID
服务器中存放的最大数据ID.
值越大说明数据越新,在选举算法中数据越新权重越大。
Epoch:逻辑时钟
或者叫投票的次数,同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断。