Zookeeper 总结
学习是一种浮躁的事情,要静下心来慢慢的品味。 -- 小徐
官网:http://zookeeper.apache.org/
概述
zookeeper 主要负责管理机器的正常运行,如果一台机器突然死掉,利用zookeeper的机制可以快速的启动另一台备份的机器,zookeeper在这一方面做出了杰出的贡献,底层实现的算法是fast paxos 与baxic paxos算法,当zookeeper失去太多的leader或者太多的follower时会进入回复的状态,进行选举。
ZNode用来描述ZooKeeper中的数据节点,它持有一个状态数据结构(stat),此结构中包含数据更新的版本号、访问权限(ACL)更新的版本号、时间戳。这些版本号和时间戳使ZooKeeper可以验证缓存有效性和协调更新。每当ZNode中的数据更新时,版本号都会递增。
ZooKeeper主要的职责为管理用户提交的数据,以及为用户程序提供数据节点监听服务。
角色
Zookeeper 有两种角色分别是leader 与follower (obsever), 在选举时机器超过一半即可存活,一般的机器配置成单个数
特性
- 每台机器上保存的数据一致,用户不管访问那台机器获取的数据信息都是一致的
- 分布式的读写,更细请求转发都是由leader转发的
- 数据更新原子性,一次数据的更新要不成功要不失败
- 每个节点在zookeeper中叫做znode,并且其有一个唯一的路径标识
- 实时性,在一定的范围内,client能读取到最新的数据。
- 数据更新原子性,一次数据更新要么成功(半数以上节点成功),要么失败
选举机制
那么,初始化的时候,是按照上述的说明进行选举的,但是当zookeeper运行了一段时间之后,有机器down掉,重新选举时,选举过程就相对复杂了,在选举时zookeeper有Fast Paxos与Basic Paxos算法来辅助选举,系统默认的选举算法为fast paxos。
需要加入数据version、leader id和逻辑时钟。
数据version:数据新的version就大,数据每次更新都会更新version。
Leader id:就是我们配置的myid中的值,每个机器一个。
逻辑时钟:这个值从0开始递增,每次选举对应一个值,也就是说: 如果在同一次选举中,那么这个值应该是一致的 ; 逻辑时钟值越大,说明这一次选举leader的进程更新.
选举的标准就变成:
1、逻辑时钟小的选举结果被忽略,重新投票
2、统一逻辑时钟后,数据id大的胜出
3、数据id相同的情况下,leader id大的胜出
根据这个规则选出leader。
Zookeeper 节点的类型
Zookeeper有两种节点四种模式,有持久的一临时的两种节点,持久节点(PERSISTENT)、持久顺序节点(PERSISTENT_SEQUENTIAL)、临时节点(EPHEMERAL)、临时顺序节点(EPHEMERAL_SEQUENTIAL)四种模式。
Zookeeper 权限详解
ZK的节点有5种操作权限:
CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、删、改、查、管理权限,这5种权限简写为crwda(即:每个单词的首字符缩写)。
代码显示所示zookeeper的权限:
Zookeeper 的部署
软件下载:链接:http://pan.baidu.com/s/1dEVRq2d 密码:ggng 如果无法下载请联系作者。
Zk链接工具:https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip
监控软件Node-zk-browser下载:链接:http://pan.baidu.com/s/1c2zf03Y 密码:4od0
1 -1 ) 、安装
[root@hadoop1 local]# tar -zxvf zookeeper-3.4.8.tar.gz
[root@hadoop1 local]# cd zookeeper-3.4.8
1-2 ) 、修改配置文件
[root@hadoop1 conf]# cp zoo_sample.cfg zoo.cfg
[root@hadoop3 conf]# cat zoo.cfg
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/usr/local/zookeeper-3.4.5/conf
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
server.1=hadoop1:2888:3888
server.2=hadoop2:2888:3888
server.3=hadoop3:2888:3888
tickTime ::表示服务器端与客户端之间的心跳的间隔。
initLimit:初始化链接急群众的leader与Follower之间的通信的时长,如果超过这个时间间隔zookeeper则会任务这个客户端死掉了。
syncLimit:表示Leader与Follower之间的发送消息,请求与答应的时间的长度,最长不超过tickTime的时间的长度,总长度为2*2000=4秒的时间。
dataDir ::是zookeeper 保存数据的目录,zookeeper的日志文件也将写入到这个文件夹中。
clientPort :对外提供的端口,默认的是2181
Server.1:hadoop1:2888:3888:1代表第几台服务器的编号,hadoop1是服务器的IP地址,2888是心跳端口,3888是选举端口。
[root@hadoop1 local]# scp -r zookeeper-3.4.8/ hadoop2:/usr/local/
[root@hadoop1 local]# scp -r zookeeper-3.4.8/ hadoop3:/usr/local/
[root@hadoop1 conf]# mkdir -p /usr/local/zookeeper-3.4.5/conf/
[root@hadoop2 conf]# mkdir -p /usr/local/zookeeper-3.4.5/conf/
[root@hadoop3 conf]# mkdir -p /usr/local/zookeeper-3.4.5/conf/
[root@hadoop1 local]# echo "1"> /usr/local/zookeeper-3.4.5/conf/myid
[root@hadoop2 local]# echo "2"> /usr/local/zookeeper-3.4.5/conf/myid
[root@hadoop3 local]# echo "3" > /usr/local/zookeeper-3.4.5/conf/myid
[root@hadoop1 local]# vi /etc/profile
export ZK_HOME=/usr/local/zookeeper-3.4.5
export PATH=$PATH:$ZK_HOME/bin
使配置文件生效
[root@hadoop1 local]# source /etc/profile
在zookeeper上默认的节点的储存大小是1M,如果想修节点的大小在zkServer.sh 文件中添加一下属性:
ZOO_USER_CFG="-Djute.maxbuffer=10240000"
详细请看:http://blog.csdn.net/xfg0218/article/details/52709917,建议不要修改的太大,因为太大为带了zookeeper的负担。
1-3 ) 、启动
在hadoop1/hadoop2/hadoop3 机器上分别启动 zkServer.sh start
[root@hadoop1 zookeeper]# zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.8/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
1-4)、一键启动&关闭脚本
[root@hadoop1 start_sh]# vi slave
hadoop1
hadoop2
hadoop3
[root@hadoop1 start_sh]# vi zookeeper.sh
#!/bin/bash
cat /usr/local/start_sh/slave |while read slave
do
{
echo $line
ssh $line "source /etc/profile;nohup zkServer.sh start"
}&
wait
done
[root@hadoop1 start_sh]# vi stop_zookeeper.sh
#!/bin/bash
cat /usr/local/start_sh/slave |while read slave
do
{
echo $line
ssh $line "source /etc/profile;nohup zkServer.sh stop"
}&
wait
done
[root@hadoop1 start_sh]# chmod a+x zookeeper.sh
[root@hadoop1 start_sh]# ./ zookeeper.sh
1-5 ) 、查看进程状态
[root@hadoop1 conf]# jps
3101 QuorumPeerMain
[root@hadoop1 conf]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.8/bin/../conf/zoo.cfg
Mode: follower
[root@hadoop2 ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.8/bin/../conf/zoo.cfg
Mode: follower
[root@hadoop3 ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper-3.4.8/bin/../conf/zoo.cfg
Mode: leader
1-6) 、节点类型
Znode是客户端访问ZooKeeper的主要实体,它包含以下几个特征:
1-1)、Watches
客户端可以在节点上设置watch(我们称之为监视器)。当节点状态发生改变时(数据的增、删、改)将会触发watch所对应的操作。当watch被触发时,ZooKeeper将会向客户端发送且仅发送一条通知,因为watch只能被触发一次。
1-2)、数据访问
ZooKeeper中的每个节点存储的数据要被原子性的操作。也就是说读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一个节点都拥有自己的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的操作。
1-3)、节点类型
ZooKeeper中的节点有两种,分别为临时节点和永久节点。节点的类型在创建时即被确定,并且不能改变。
ZooKeeper的临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话结束,临时节点将被自动删除,当然可以也可以手动删除。另外,需要注意是, ZooKeeper的临时节点不允许拥有子节点。
ZooKeeper的永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。
1-4)、顺序节点(唯一性的保证)
当创建Znode的时候,用户可以请求在ZooKeeper的路径结尾添加一个递增的计数。这个计数对于此节点的父节点来说是唯一的,它的格式为"%10d"(10位数字,没有数值的数位用0补充,例如"0000000001")。当计数值大于232-1时,计数器将溢出。
org.apache.zookeeper.CreateMode中定义了四种节点类型,分别对应:
PERSISTENT:永久节点
EPHEMERAL:临时节点
PERSISTENT_SEQUENTIAL:永久节点、序列化
EPHEMERAL_SEQUENTIAL:临时节点、序列化
1-1)、dataVersion
数据版本号,每次对节点进行set操作,dataVersion的值都会增加1(即使设置的是相同的数据)。
1-2)、cversion
子节点的版本号。当znode的子节点有变化时,cversion 的值就会增加1。
1-3)、aclVersion
ACL的版本号,关于znode的ACL(Access Control List,访问控制),可以参考资料1 有关ACL的描述。
1-7 ) 、zookerper 的客户端连接
1-1)、查看帮助信息
运行 zkCli.sh –server <ip>进入命令行工具
[root@hadoop1 conf]# zkCli.sh -server hadoop1
**********
Welcome to ZooKeeper!
2016-10-03 07:58:35,247 [myid:] - INFO [main-SendThread(hadoop1:2181):ClientCnxn$SendThread@1032] - Opening socket connection to server hadoop1/192.168.215.134:2181. Will not attempt to authenticate using SASL (unknown error)
JLine support is enabled
2016-10-03 07:58:35,510 [myid:] - INFO [main-SendThread(hadoop1:2181):ClientCnxn$SendThread@876] - Socket connection established to hadoop1/192.168.215.134:2181, initiating session
2016-10-03 07:58:36,231 [myid:] - INFO [main-SendThread(hadoop1:2181):ClientCnxn$SendThread@1299] - Session establishment complete on server hadoop1/192.168.215.134:2181, sessionid = 0x1578a466e400001, negotiated timeout = 30000
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
// 查看命令帮助
[zk: hadoop1(CONNECTED) 0] list
ZooKeeper -server host:port cmd args
connect host:port
get path [watch]
ls path [watch]
set path data [version]
rmr path
delquota [-n|-b] path
quit
printwatches on|off
create [-s] [-e] path data acl
stat path [watch]
close
ls2 path [watch]
history
listquota path
setAcl path acl
getAcl path
sync path
redo cmdno
addauth scheme auth
delete path [version]
setquota -n|-b val path
1-2)、创建节点
[zk: localhost:2181(CONNECTED) 11] create /testzookeeper "xiaozhang"
Created /testzookeeper
1-3)、查看当前节点是否有子节点
[zk: localhost:2181(CONNECTED) 15] ls /testzookeeper
[]
1-4)、查看数据节点的信息
[zk: hadoop1(CONNECTED) 3] get /testzookeeper
// 节点的数据
"xiaozhang"
// 表示该ZNode被创建时的事务ID
cZxid = 0x500000001f
// Created Time,表示该ZNode被创建的时间
ctime = Sun Oct 02 19:46:06 PDT 2016
// Modified ZXID,表示该ZNode最后一次被更新时的事务ID
mZxid = 0x500000001f
// Modified Time,表示该节点最后一次被更新的时间
mtime = Sun Oct 02 19:46:06 PDT 2016
// 表示该节点的子节点列表最后一次被修改时的事务ID。注意,只有子节点列表变更了才会变更pZxid,子节点内容变更不会影响pZxid
pZxid = 0x5000000023
// 子节点的版本号
cversion = 2
// 数据节点的版本号
dataVersion = 0
// ACL版本号(权限的版本)
aclVersion = 0
// 创建该节点的会话的seddionID。如果该节点是持久节点,那么这个属性值为0。
ephemeralOwner = 0x0
// 数据内容的长度
dataLength = 0
// 子节点的个数
numChildren = 2
// 对spark的子节点数据的变化进行了监听
[zk: hadoop1(CONNECTED) 4] ls /spark watch
[master_status, leader_election]
// 获取spark子节点的变化监听
[zk: hadoop1(CONNECTED) 5] get /spark watch
cZxid = 0x500000001f
ctime = Sun Oct 02 19:46:06 PDT 2016
mZxid = 0x500000001f
mtime = Sun Oct 02 19:46:06 PDT 2016
pZxid = 0x5000000023
cversion = 2
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 2
1-5)、查看数据版本修改的问题
[zk: localhost:2181(CONNECTED) 17] set /testzookeeper "xiaowang"
cZxid = 0x2f00000007
ctime = Thu Nov 24 06:16:33 PST 2016
mZxid = 0x2f0000000a
mtime = Thu Nov 24 06:22:09 PST 2016
pZxid = 0x2f00000007
cversion = 0
// 数据的版本
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 0
1-6)、wacth的使用
Watcher(事件监听器),是ZooKeeper中一个很重要的特性。ZooKeeper允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper服务端会将事件通知到感兴趣的客户端上去。该机制是ZooKeeper实现分布式协调服务的重要特性。
A)、原始数据
[zk: localhost:2181(CONNECTED) 18] get /testzookeeper watch
"xiaowang"
cZxid = 0x2f00000007
ctime = Thu Nov 24 06:16:33 PST 2016
mZxid = 0x2f0000000a
mtime = Thu Nov 24 06:22:09 PST 2016
pZxid = 0x2f00000007
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 0
[zk: localhost:2181(CONNECTED) 19]
B)、修改数据
[zk: localhost:2181(CONNECTED) 3] set /testzookeeper "xiaoming"
cZxid = 0x2f00000007
ctime = Thu Nov 24 06:16:33 PST 2016
mZxid = 0x2f0000000b
mtime = Thu Nov 24 06:28:02 PST 2016
pZxid = 0x2f00000007
cversion = 0
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 0
C)、查看修改的事件
[zk: localhost:2181(CONNECTED) 18] get /testzookeeper watch
"xiaowang"
cZxid = 0x2f00000007
ctime = Thu Nov 24 06:16:33 PST 2016
mZxid = 0x2f0000000a
mtime = Thu Nov 24 06:22:09 PST 2016
pZxid = 0x2f00000007
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 10
numChildren = 0
[zk: localhost:2181(CONNECTED) 19]
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/testzookeeper
对于监听的事件只监听一次
1-7)、watch 过程详解
- 、当客户端请求时并添加了wacth的时候
- 、会开启listen(监听) thread线程来监听事件,底层实现的也是scoker进行通信的
- 、在集群中收到监听的IP以及端口的信息,并这这些信息传递给其他的机器上、
1-8)、ACL 概念
ZooKeeper采用ACL(Access Control Lists)策略来进行权限控制。ZooKeeper定义了如下5种权限。
CREATE: 创建子节点的权限。
READ: 获取节点数据和子节点列表的权限。
WRITE:更新节点数据的权限。
DELETE: 删除子节点的权限。
ADMIN: 设置节点ACL的权限。
注意:CREATE 和 DELETE 都是针对子节点的权限控制。
1-9)、查看当前的ZNode的节点数
[zk: localhost:2181(CONNECTED) 1] ls /
[testZookeeper, hadoop-ha, hbase, admin, zookeeper, consumers, config, spark, yarn-leader-election, brokers, controller_epoch]
1-10)、删除一个ZNode 节点
[zk: localhost:2181(CONNECTED) 2] delete /admin
1-11)、删除一个Child znode
[zk: localhost:2181(CONNECTED) 3] ls /admin
[delete_topics]
[zk: localhost:2181(CONNECTED) 4] delete /admin/delete_topics
1-12)、递归删除ZNode
[zk: localhost:2181(CONNECTED) 4] rmr /admin
链接工具:链接:http://pan.baidu.com/s/1eSgEfPg 密码:g1bb 无法下载请联系作者。
进入到ZooInspector的build的目录会看到一个JAR包,执行以下命令
之前有保存的信息,所以会显示的多一点。
1-8)、查看zookeeper版本的控制
[root@hadoop1 /]# cd /usr/local/zookeeper-3.4.5/conf/version-2
[root@hadoop1 version-2]# ls
acceptedEpoch log.200000001 log.2d00000001 log.500000001 snapshot.16000002b3 snapshot.1800000002 snapshot.1c0000001d
currentEpoch log.2700000045 log.300000054 snapshot.1300000300 snapshot.1700000056 snapshot.1900000017 snapshot.2c00000098
acceptedEpoch: 在集群中都被认可的元数据
currentEpoch:当前机器的元数据
Snapshot:参考物件的版本
Zookeeper 内存数据库
1-1)、ZKDatabase 内存数据库的实质载体
搜索zkDatabase的类即可查看初始化的信息
1-2)、详解DataTree
DateTree 是zookeeper中的核心代码,代表了内存中核心的数据,通过下面可以看出dataTree维护了两个并行的数据结构,一个是hash表,一个是数据节点的构成的树,所有的访问都是通过哈希表来映射查找数据的,如果数据已经加载到磁盘上才会去遍历。
Nodes : 用于快速查找数据节点的并发哈希表。
Key:数据节点路径
Value:DataNode
这个数据结构是专门用来存放临时节点的,方便实时访问和及时清理
1-3)、DataNode 类的属性
parent:父节点信息
data[]:数据信息
acl:访问控制信息
stat:持久化到磁盘上的统计信息
children:子节点信息
Zookeeper 的客户端的API的使用
1 -1 ) 、基本方法
功能 描述
create 在本地目录树中创建一个节点
delete 删除一个节点
exists 测试本地是否存在目标节点
get/set data 从目标节点上读取?/?写数据
get/set ACL 获取?/?设置目标节点访问控制列表信息
get children 检索一个子节点上的列表
sync 等待要被传送的数据
1-2 )、 增删改查znode数据
先在编辑器中导入zookeeper-3.4.5\lib的JAR包,还有一个是zookeeper根目录下的zookeeper-3.4.5.jar
package zookeeperTest;
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
/**
* zookeeper的基本的操作
*/
public class ZookeeperSimple {
// 会话超时时间,设置为与系统默认时间一致
private static final int SESSION_TIMEOUT = 30000;
// 创建 ZooKeeper 实例
ZooKeeper zk;
// 创建 Watcher 实例
Watcher wh = new Watcher() {
public void process(org.apache.zookeeper.WatchedEvent event) {
System.out.println(event.toString());
}
};
/**
* 初始化zookeeper的操作
*/
private void createZKInstance() throws IOException {
zk = new ZooKeeper("hadoop1:2181", ZookeeperSimple.SESSION_TIMEOUT,
this.wh);
}
/**
* zookeeper的基本的操作
*
* @throws IOException
* @throws InterruptedException
* @throws KeeperException
*/
private void ZKOperations() throws IOException, InterruptedException,
KeeperException {
// 创建节点
zk.create("/testZookeeper", "mytestZookeeper".getBytes(),
Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// 删除节点
System.out
.println(new String(zk.getData("/testZookeeper", false, null)));
// 修改节点
zk.setData("/testZookeeper", "zookeeperModify".getBytes(), -1);
// 获取修改节点的状态
System.out
.println(new String(zk.getData("/testZookeeper", false, null)));
// 删除节点
zk.delete("/testZookeeper", -1);
// 查看删除节点的状态
System.out.println("节点状态:" + zk.exists("/testZookeeper", false));
}
/**
* 关闭zookeeper的链接
*/
private void ZKClose() throws InterruptedException {
zk.close();
}
public static void main(String[] args) throws IOException,
InterruptedException, KeeperException {
ZookeeperSimple dm = new ZookeeperSimple();
dm.createZKInstance();
dm.ZKOperations();
dm.ZKClose();
}
}
1-3)、zookeeper服务器上下线动态感知
在分布式系统中,主节点可以有多台,可以动态上下线,任意一台客户端都能实时感知到主节点服务器的上下线。
需求让客户端动态感知在线的服务器的运行状态。
1-1)、服务器端
获取链接 ( process )
利用zookeeper链接注册服务器的信息
启动业务的逻辑实现
服务器端不需要实现监听
1-2)、客户端
获取链接(process)
获取server上的子节点的信息,并从中获服务器的信息列表
业务启动线程
1-1)、服务端代码实现
package zookeeperPerception;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
/**
* 动态感知Server
*/
public class ZookeeperServer {
private String groupNode = "sgroup";
private String subNode = "sub";
/**
* 连接zookeeper
*
* @param address
* server的地址
*/
public void connectZookeeper(String address) throws Exception {
ZooKeeper zk = new ZooKeeper("hadoop1:2181,hadoop2:2181,hadoop3:2181",
5000, new Watcher() {
public void process(WatchedEvent event) {
// 不做处理
}
});
// 在"/sgroup"下创建子节点
// 子节点的类型设置为EPHEMERAL_SEQUENTIAL, 表明这是一个临时节点, 且在子节点的名称后面加上一串数字后缀
// 将server的地址数据关联到新创建的子节点上
String createdPath = zk.create("/" + groupNode,
address.getBytes("utf-8"), Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("create: " + createdPath);
}
/**
* server的工作逻辑写在这个方法中 此处不做任何处理, 只让server sleep
*/
public void handle() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
ZookeeperServer as = new ZookeeperServer();
as.connectZookeeper("server1");
as.handle();
}
}
1-2)、客户端代码实现
package zookeeperPerception;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/**
* 动态感知Client
*/
public class ZookeeperClient {
private String groupNode = "sgroup";
private ZooKeeper zk;
private Stat stat = new Stat();
private volatile List<String> serverList;
/**
* 连接zookeeper
*/
public void connectZookeeper() throws Exception {
zk = new ZooKeeper("hadoop1:2181,hadoop2:2181,hadoop3:2181", 5000,
new Watcher() {
public void process(WatchedEvent event) {
// 如果发生了"/sgroup"节点下的子节点变化事件, 更新server列表, 并重新注册监听
if (event.getType() == EventType.NodeChildrenChanged
&& ("/" + groupNode).equals(event.getPath())) {
try {
updateServerList();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
updateServerList();
}
/**
* 更新server列表
*/
private void updateServerList() throws Exception {
List<String> newServerList = new ArrayList<String>();
// 获取并监听groupNode的子节点变化
// watch参数为true, 表示监听子节点变化事件.
// 每次都需要重新注册监听, 因为一次注册, 只能监听一次事件, 如果还想继续保持监听, 必须重新注册
List<String> subList = zk.getChildren("/" + "sgroup0000000039", true);
for (String subNode : subList) {
// 获取每个子节点下关联的server地址
byte[] data = zk.getData("/" + "sgroup0000000039", false, stat);
newServerList.add(new String(data, "utf-8"));
}
// 替换server列表
serverList = newServerList;
System.out.println("server list updated: " + serverList.size());
}
/**
* client的工作逻辑写在这个方法中 此处不做任何处理, 只让client sleep
*/
public void handle() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
ZookeeperClient ac = new ZookeeperClient();
ac.connectZookeeper();
ac.handle();
}
}
https://github.com/alibaba/taokeeper/tree/master/taokeeper-research
1-4) 、分布式共享锁
在我们自己的分布式业务系统中,可能会存在某种资源,需要被整个系统的各台服务器共享访问,但是只允许一台服务器同时访问
先查看所有机器上的锁,查看自己的锁是不是最小的,如果是最小的则会访问最大的那把锁,并把数据同步下来,访问完后把自己的锁删掉后,再会重新注册一把新锁。
package lock;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class DistributedClientLock {
// 会话超时
private static final int SESSION_TIMEOUT = 2000;
// zookeeper集群地址
private String hosts = "hadoop1:2181,hadoop2:2181,hadoop3:2181";
private String groupNode = "locks";
private String subNode = "sub";
private boolean haveLock = false;
private ZooKeeper zk;
// 记录自己创建的子节点路径
private volatile String thisPath;
/**
* 连接zookeeper
*/
public void connectZookeeper() throws Exception {
zk = new ZooKeeper(hosts, SESSION_TIMEOUT, new Watcher() {
public void process(WatchedEvent event) {
try {
// 判断事件类型,此处只处理子节点变化事件
if (event.getType() == EventType.NodeChildrenChanged
&& event.getPath().equals("/" + groupNode)) {
// 获取子节点,并对父节点进行监听
List<String> childrenNodes = zk.getChildren("/"
+ groupNode, true);
String thisNode = thisPath
.substring(("/" + groupNode + "/").length());
// 去比较是否自己是最小id
Collections.sort(childrenNodes);
if (childrenNodes.indexOf(thisNode) == 0) {
// 访问共享资源处理业务,并且在处理完成之后删除锁
doSomething();
// 重新注册一把新的锁
thisPath = zk.create("/" + groupNode + "/"
+ subNode, null, Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
// 1、程序一进来就先注册一把锁到zk上
thisPath = zk.create("/" + groupNode + "/" + subNode, null,
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
// wait一小会,便于观察
Thread.sleep(new Random().nextInt(1000));
// 从zk的锁父目录下,获取所有子节点,并且注册对父节点的监听
List<String> childrenNodes = zk.getChildren("/" + groupNode, true);
// 如果争抢资源的程序就只有自己,则可以直接去访问共享资源
if (childrenNodes.size() == 1) {
doSomething();
thisPath = zk.create("/" + groupNode + "/" + subNode, null,
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
}
}
/**
* 处理业务逻辑,并且在最后释放锁
*/
private void doSomething() throws Exception {
try {
System.out.println("gain lock: " + thisPath);
Thread.sleep(2000);
// do something
} finally {
System.out.println("finished: " + thisPath);
//
// 访问完毕后,需要手动去删除之前的锁节点,-1代表删除所有的版本的 记录信息。
zk.delete(this.thisPath, -1);
}
}
public static void main(String[] args) throws Exception {
DistributedClientLock dl = new DistributedClientLock();
dl.connectZookeeper();
Thread.sleep(Long.MAX_VALUE);
}
}
Node-zk-browser 安装
软件下载:链接:http://pan.baidu.com/s/1c2zf03Y 密码:4od0 如果无法下载,请联系作者。
A)、准备环境,nodes需要gcc环境
[root@hadoop1 /]# yum -y install gcc make gcc-c++ openssl-devel
B)、安装 nodes
[root@hadoop1 /]# wget http://nodejs.org/dist/v0.10.26/node-v0.10.26.tar.gz
C)、解压文件
[root@hadoop1 /]#tar -zxvf node-v0.10.26.tar.gz
D)、编辑文件
[root@hadoop1 node-v0.10.26]# make && make install
E)、查看node版本
[root@hadoop1 node-v0.10.26]# node -v
v0.10.26
F)、下载node-zk-browser
[root@hadoop1 zookeeperWeb]# wget https://github.com/killme2008/node-zk-browser/archive/master.zip
G)、安装node-zk-browser
[root@hadoop2 node-zk-browser-master]# npm install -d
H)、安装zk的版本
[root@hadoop1 node-zk-browser-master]# npm install zookeeper
I)、修改配置文件
[root@hadoop1 node-zk-browser-master]# vi app.js
找到以下配置修改
var host = process.env.ZK_HOST || 'hadoop1:2181,hadoop2:2181,hadoop3:2181';
[root@hadoop2 node-zk-browser-master]# vi user.json
修改一下配置
{
"admin" : "admin"
}
J)、启动服务
[root@hadoop1 node-zk-browser-master]# ./start.sh
L)、界面展示
查看运行的Log发现运行的端口是3000
[root@hadoop1 logs]# vi node-zk-browser.log
********
Express server listening on port 3000
Caught exception: Error: listen EADDRINUSE
zk session established, id=15b4374b7a60002
http://hadoop1:3000
可以点击Signin登录,用户名与密码都是admin