一、Zookeeper数据查看工具ZooInspector简介
一、背景
Zookeeper作为常用的集群协调者组件被广泛应用,尤其是在大数据生态圈中;
Zookeeper集群存储各个节点信息,包括:Hadoop、Hbase、Storm、Kafka等等;
二、查询ZK数据的方式
那如何查看Zookeeper中的数据呢,我们可以通过ZkCli.sh命令客户端查看,但是不太直观,因为ZK本身数据是以树型结构存储组织的,
所以今天推荐一个实用的界面操作工具ZooInspector;
三、ZooInspector的使用
1、下载https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip;
2、解压,进入目录ZooInspector\build,运行zookeeper-dev-ZooInspector.jar;
java -jar zookeeper-dev-ZooInspector.jar //执行成功后,会弹出java ui client
3、点击左上角连接按钮,输入zk服务地址:ip:2181
点击OK,即可查看ZK节点信息
二、zookeeper的安装与集群
zookeeper简介
Zookeeper是一个分布式协调服务;就是为用户的分布式应用程序提供协调服务
A、zookeeper是为别的分布式程序服务的
B、Zookeeper本身就是一个分布式程序(只要有半数以上节点存活,zk就能正常服务)
C、Zookeeper所提供的服务涵盖:主从协调、服务器节点动态上下线、统一配置管理、分布式共享锁、统一名称服务……
D、虽然说可以提供各种服务,但是zookeeper在底层其实只提供了两个功能:
管理(存储,读取)用户程序提交的数据;
并为用户程序提供数据节点监听服务;
Zookeeper集群的角色: Leader 和 follower (Observer)
只要集群中有半数以上节点存活,集群就能提供服务
1、zookeeper的安装
这里我准备了一个纯净的linux来进行安装,另外zookeeper是java开发的所以先装好jdk(1.7及以上)。
1、将zookeeper-3.4.6.tar.gz上传到/root目录下。
2、创建apps文件夹,将zookeeper-3.4.6.tar.gz解压到apps文件夹下。
[root@localhost ~]# ll
总用量 17312
-rw-------. 1 root root 1131 9月 12 03:59 anaconda-ks.cfg
-rw-r--r--. 1 root root 12526 9月 12 03:59 install.log
-rw-r--r--. 1 root root 3482 9月 12 03:59 install.log.syslog
-rw-r--r--. 1 root root 17699306 6月 20 15:55 zookeeper-3.4.6.tar.gz
[root@localhost ~]# mkdir apps
[root@localhost ~]# tar -zxvf zookeeper-3.4.6.tar.gz -C apps/
3、进入apps/zookeeper-3.4.6.tar.gz/conf文件夹,复制zoo_example.cfg改名为zoo.cfg
[root@localhost apps]# ll
总用量 4
drwxr-xr-x. 10 1000 1000 4096 2月 20 2014 zookeeper-3.4.6
[root@localhost apps]# cd zookeeper-3.4.6/conf
[root@localhost conf]# ll
总用量 12
-rw-rw-r--. 1 1000 1000 535 2月 20 2014 configuration.xsl
-rw-rw-r--. 1 1000 1000 2161 2月 20 2014 log4j.properties
-rw-rw-r--. 1 1000 1000 922 2月 20 2014 zoo_sample.cfg
[root@localhost conf]# cp zoo_sample.cfg zoo.cfg
[root@localhost conf]# ll
总用量 16
-rw-rw-r--. 1 1000 1000 535 2月 20 2014 configuration.xsl
-rw-rw-r--. 1 1000 1000 2161 2月 20 2014 log4j.properties
-rw-r--r--. 1 root root 922 9月 12 18:05 zoo.cfg
-rw-rw-r--. 1 1000 1000 922 2月 20 2014 zoo_sample.cfg
4、修改配置文件zoo.cfg,这里只修改数据存储文件夹位置
这里写图片描述
5、创建文件夹 /root/zkdata
[root@localhost conf]# cd /root
[root@localhost ~]# mkdir zkdata
[root@localhost ~]# ll
总用量 17320
-rw-------. 1 root root 1131 9月 12 03:59 anaconda-ks.cfg
drwxr-xr-x. 3 root root 4096 9月 12 18:04 apps
-rw-r--r--. 1 root root 12526 9月 12 03:59 install.log
-rw-r--r--. 1 root root 3482 9月 12 03:59 install.log.syslog
drwxr-xr-x. 2 root root 4096 9月 12 18:12 zkdata
-rw-r--r--. 1 root root 17699306 6月 20 15:55 zookeeper-3.4.6.tar.gz
6、进入/root/zookeeper-3.4.6/bin,启动zookeeper并且查看是否启动成功。
[root@localhost bin]# ll
总用量 36
-rwxr-xr-x. 1 1000 1000 238 2月 20 2014 README.txt
-rwxr-xr-x. 1 1000 1000 1937 2月 20 2014 zkCleanup.sh
-rwxr-xr-x. 1 1000 1000 1049 2月 20 2014 zkCli.cmd
-rwxr-xr-x. 1 1000 1000 1534 2月 20 2014 zkCli.sh
-rwxr-xr-x. 1 1000 1000 1333 2月 20 2014 zkEnv.cmd
-rwxr-xr-x. 1 1000 1000 2696 2月 20 2014 zkEnv.sh
-rwxr-xr-x. 1 1000 1000 1084 2月 20 2014 zkServer.cmd
-rwxr-xr-x. 1 1000 1000 5742 2月 20 2014 zkServer.sh
[root@localhost bin]# ./zkServer.sh start
JMX enabled by default
Using config: /root/apps/zookeeper-3.4.6/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@localhost bin]# ./zkServer.sh status
JMX enabled by default
Using config: /root/apps/zookeeper-3.4.6/bin/../conf/zoo.cfg
Mode: standalone
到这就说明启动成功了。但是实际有可能会出现一些问题。比如查看状态就经常看到未启动成功
[root@itheima32 bin]# ./zkServer.sh status
JMX enabled by default
Using config: /usr/zookeeper-3.4.6/bin/../conf/zoo.cfg
Error contacting service. It is probably not running.
先关闭防火墙试试
[root@itheima32 bin]# service iptables stop
iptables:清除防火墙规则: [确定]
iptables:将链设置为政策 ACCEPT:filter [确定]
iptables:正在卸载模块: [确定]
或者
chkconfig iptables off(永久关闭(开机不启动防火墙))
如果不行,那么查看日志吧。
[root@localhost bin]# cat zookeeper.out
如果出现了java.net.BindException: Address already in use
地址被占用,那么先查看是否被占用,占用了则杀死进程再启动,建议去/root/zkdata下删除zookeeper_server.pid。
这里写图片描述
2、zookeeper的集群
zookeeper的安装很简单,zookeeper的集群也不难,主要还是改配置,准备三个纯净版的linux,还是得装好jdk配置好环境
可以使用前面用的linux,把apps文件夹和zkdata文件夹都删了即可。然后克隆两个,克隆了一定需要修改ip地址。但是可能会出现下面的问题。
(1)、查看ip的话看不到eth0只显示127.0.0.1,出现这种情况使用以下命令
ifup eth0 ifconfig up eth0
(2)、如果使用上面的命令出现了device eth0 does not seem to be present, delaying initialization问题。可以使用下面两个步骤解决。
第一步:删除mac地址
这里写图片描述
第二步:删除70-persistentce-net.rules
这里写图片描述
这个文件绑定了网卡和mac地址,所以换了网卡以后MAC地址变了,然后直接删除重启,它会自动生成个。
重启后再用ifup eth0 ifconfig up eth0命令就能看到了。
zookeeper的集群
1、首先使用的是192.168.25.126这台服务器,前面6步与zookeeper安装一致,唯一区别就是配置文件修改不一样
这里写图片描述
注:2888是leader与follower之间通信的端口,3888是投票使用的端口(数字不做要求,但应避免冲突)。
2、将配置好的apps下的zookeeper传到另外两台linux
[root@localhost ~]# scp -r apps 192.168.25.127:/root
root@192.168.25.127's password:
...
[root@localhost ~]# scp -r apps 192.168.25.125:/root
root@192.168.25.127's password:
...
3、zkdata文件夹下创建myid文件设置值为2
[root@localhost zkdata]# echo 2 > myid
[root@localhost zkdata]# cat myid
2
4、分别切换至192.168.25.127和192.168.25.125两台服务器,同样创建zkdata,zkdata下创建myid文件设置值分别为1,3。
注意:myid里面设置的值一定要与配置文件里设定的一致。
5、再分别启动三条服务器,查看是否启动成功
[root@localhost bin]# ./zkServer.sh status
JMX enabled by default
Using config: /root/apps/zookeeper-3.4.6/bin/../conf/zoo.cfg
Mode: follower
[root@localhost bin]# ./zkServer.sh status
JMX enabled by default
Using config: /root/apps/zookeeper-3.4.6/bin/../conf/zoo.cfg
Mode: leader
[root@localhost bin]# ./zkServer.sh status
JMX enabled by default
Using config: /root/apps/zookeeper-3.4.6/bin/../conf/zoo.cfg
Mode: follower
一个leader两个follower
如果启动的了查看状态发现没有启动成功。除了zookeeper安装时候出现的问题,这里主要的问题就是zkdata文件夹下的myid的值写的跟配置文件里面不一致,这个时候会出现ConnectionException:拒绝连接的异常,检查修改一下。如果是地址被占用那么就用上面介绍的方法。
停止leader,看看是否能选举出新的leader
三、使用java api操作zookeeper
ZooKeeper服务命令:
在准备好相应的配置之后,可以直接通过zkServer.sh 这个脚本进行服务的相关操作
1. 启动ZK服务: sh bin/zkServer.sh start
2. 查看ZK服务状态: sh bin/zkServer.sh status
3. 停止ZK服务: sh bin/zkServer.sh stop
4. 重启ZK服务: sh bin/zkServer.sh restart
zk客户端命令
ZooKeeper命令行工具类似于Linux的shell环境,我们可以简单的对ZooKeeper进行访问,数据创建,数据修改等操作. 使用 zkCli.sh -server 127.0.0.1:2181 连接到 ZooKeeper 服务,连接成功后,系统会输出 ZooKeeper 的相关环境以及配置信息。
命令行工具的一些简单操作如下:
1. 显示根目录下、文件: ls / 使用 ls 命令来查看当前 ZooKeeper 中所包含的内容
2. 显示根目录下、文件: ls2 / 查看当前节点数据并能看到更新次数等数据
3. 创建文件,并设置初始内容: create /zk "test" 创建一个新的 znode节点“ zk ”以及与它关联的字符串
4. 获取文件内容: get /zk 确认 znode 是否包含我们所创建的字符串
5. 修改文件内容: set /zk "zkbak" 对 zk 所关联的字符串进行设置
6. 删除文件: delete /zk 将刚才创建的 znode 删除
7. 退出客户端: quit
8. 帮助命令: help
创建maven项目,导入zookeeper相关包
<dependencies>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.7</version>
</dependency>
</dependencies>
1、连接zookeeper
public class TestCase {
private static final String connectionString = "192.168.25.127:2181,"
+ "192.168.25.129:2181,"
+ "192.168.25.130:2181";
public static final Integer sessionTimeout = 2000;
public static ZooKeeper zkClient = null;
@Before
public void init() throws Exception{
//三个参数分别为连接的zookeeper集群服务器的ip,超时时间,监听器
zkClient = new ZooKeeper(connectionString, sessionTimeout, new Watcher(){
//收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
public void process(WatchedEvent event) {
System.out.println(event.getType()+","+event.getPath());
}});
}
命令行查看
[zk: localhost:2181(CONNECTED) 1] ls /
[app1, idea, test, servers, zookeeper]
2、创建数据节点到zk中
@Test
public void createNode() throws Exception{
/*
* 传入四个参数
* 1、创建的节点
* 2、节点数据
* 3、节点的权限,OPEN_ACL_UNSAFE表示内部应用权限
* 4、节点类型,4种:持久化节点,带序列持久化节点,临时节点,带序列的临时节点
*/
String path = zkClient.create("/idea",
"helloworld".getBytes(),
Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
System.out.println(path);
}
3、获取子节点
@Test
public void getChildren() throws Exception{
/*
* 传入2个参数
* 1、指定获取哪个节点的孩子
* 2、是否使用监听器(watcher),true表示使用以上的监听功能
*/
List<String> children = zkClient.getChildren("/",true);
for (String child : children) {
System.out.println(child);
}
System.in.read();
}
控制台输出
None,null
app1
idea
test
zookeeper
servers
由于使用了监听功能,那么可以测试下监听功能
使用命令行删除掉节点/idea
控制台会继续输出
None,null
app1
idea
test
zookeeper
servers
NodeChildrenChanged,/ 继续输出的内容
但是如果再创建该节点,控制台不会再输出任何内容,这是因为监听是一次性的。监听到了删除节点的事件后便不能监听到创建节点。
为了能持续监听需要对监听处理逻辑做修改
public void process(WatchedEvent event) {
//收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
System.out.println(event.getType()+"---"+event.getPath());
//为了能一直监听,调用一次注册一次
try {
zkClient.getChildren("/", true);
} catch (Exception e) {
e.printStackTrace();
}
}
4、判断节点是否存在
@Test
public void testExist() throws Exception{
//一个参数是节点,一个是是否用监听功能,Stat封装了该节点的相关信息比如:czxid,mzxid,ctime,mtime等
Stat stat = zkClient.exists("/idea", false);
System.out.println(stat==null?"不存在":"存在");
}
5、获取节点数据
@Test
public void getData() throws Exception{
byte[] data = zkClient.getData("/idea", false, null);
System.out.println(new String(data));
}
输出:
None,null
helloworld
6、删除节点
@Test
public void delete() throws Exception{
//第一个参数为要删除的节点,第二个参数表示版本,-1表示所有版本
zkClient.delete("/idea",-1);
}
7、修改节点数据
@Test
public void update() throws Exception{
//原 /idea节点的数据为helloworld
zkClient.setData("/idea", "zookeeper".getBytes(), -1);
//查看修改数据是否成功
byte[] data = zkClient.getData("/idea", false, null);
System.out.println(new String(data));
}
控制台输出:
None,null
zookeeper
监听机制
参考https://blog.youkuaiyun.com/liu857279611/article/details/70495413
版本信息
参考https://blog.youkuaiyun.com/u012831423/article/details/82795563
四、zookeeper实现分布式应用系统服务器上下线动态感知程序、监听机制与守护线程
需求
在分布式系统中存在多个服务器,这些服务器可以动态上下线,而客户端可以连接任意服务器,但是如果连接的服务器突然下线那么客户端需要重新连接其他服务器,这就需要在服务器上下线的时候客户端能感知,获取哪些可以连接的服务器。
解决思路
每次服务器启动的时候去zookeeper上进行注册(注册规则自由指定,比如简单使用/servers/server001 hostname),而客户端上线就获取服务器列表,并对节点进行监听,一旦有服务器下线那么就能监听到事件从而重新获取服务器列表。
这里写图片描述
程序简单实现
服务器端:
/**
* 服务端程序
* @author
*
*/
public class DistributedServer {
private static final String connectionString = "192.168.47.141:2181";
public static final Integer sessionTimeout = 2000;
public static ZooKeeper zkClient = null;
/**
* 获取zookeeper连接
* @throws Exception
*/
public void getConnection() throws Exception{
zkClient = new ZooKeeper(connectionString, sessionTimeout, new Watcher(){
//收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
public void process(WatchedEvent event) {
System.out.println(event.getType()+","+event.getPath());
try {
//为了能一直监听,调用一次注册一次
zkClient.getChildren("/", true);
}catch(Exception e){
}
}});
}
/**
* 注册服务器信息
* @param hostname 注册的服务器名
* @throws Exception
*/
public void registerServer(String hostname) throws Exception{
//创建的是带序号的临时节点 生成的节点像/servers/server000001,/servers/server000002等
//节点数据即为注册的主机名
String path = zkClient.create("/server", hostname.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname+" --上线了-- "+path);
}
/**
* 服务器注册完后,执行业务逻辑
* @param hostname
* @throws IOException
*/
public void executeBusiness(String hostname) throws IOException {
System.out.println(hostname+"开始工作了!");
System.in.read();
}
public static void main(String[] args) throws Exception {
//获取zookeeper连接
DistributedServer server = new DistributedServer();
server.getConnection();
//服务器上线,完成注册
Scanner scanner = new Scanner(System.in);
System.out.println("输入hostname");
String hostname = scanner.nextLine();
server.registerServer(hostname);
//执行业务逻辑
server.executeBusiness(hostname);
}
}
客户端:
/*
* 客户端程序
*/
public class DistributeClient {
private static final String connectionString = "192.168.47.141:2181";
public static final Integer sessionTimeout = 2000;
public static ZooKeeper zkClient = null;
public static final String parentNode = "/";
//注意:加volatile的意义何在?使得多线程看到的服务器列表一致而不会拷贝到自己的工作空间
public volatile List<String> serverList = new ArrayList<String>();
/**
* 获取zookeeper连接
* @throws Exception
*/
public void getConnection() throws Exception{
zkClient = new ZooKeeper(connectionString, sessionTimeout, new Watcher(){
//收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
public void process(WatchedEvent event) {
System.out.println(event.getType()+","+event.getPath());
try {
//重新获取(更新)服务器列表,并进行监听
getServerList();
}catch(Exception e){
}
}});
}
/**
* 获取服务器列表信息,并对父节点进行监听
* @throws Exception
*/
public void getServerList() throws Exception{
//获取服务器列表,并对父节点进行监听
//getChildren()相对于命令行 ls /znode,对子节点进行监听
List<String> children = zkClient.getChildren(parentNode, true);
//创建临时集合,将子节点存入
List<String> childrenList = new ArrayList<String>();
for (String child : children) {
byte[] data = zkClient.getData(parentNode+child, false, null);
childrenList.add(new String(data));
}
//将临时集合中的节点赋给服务器列表serverList,以便业务线程使用
serverList = childrenList;
System.out.println(serverList);
}
/**
* 业务功能
* @throws Exception
*/
public void executeBusiness() throws Exception{
System.out.println("获取的服务器列表:"+serverList);
System.out.println("客户端开始工作了...");
System.in.read();
}
public static void main(String[] args) throws Exception {
//获取zookeeper连接
DistributeClient client = new DistributeClient();
client.getConnection();
//获取服务器列表
client.getServerList();
//业务功能
client.executeBusiness();
}
}
测试
运行三次服务器端程序,输入的hostname分别为mini1,mini2,mini3当成注册了三个服务器
这里写图片描述
这里写图片描述
这里写图片描述
运行客户端程序(可以启动多次,简单起见这里就一次)
这里写图片描述
关闭其中2个(mini1,mini2)连接zookeeper的客户端(关闭后注册的服务器也就消失了),查看客户端输出
这里写图片描述
一旦服务器下线了,客户端能监听到并且重新获取服务器列表。