文章目录
概念
- Zookeeper是Apache Hadoop项目下的一个子项目,是一个树形目录服务
- Zookeeper是一个分布式的、开源的分布式应用程序的协调服务
Zookeeper提供的主要功能包括:
- 配置管理
在多个应用程序(或服务器)中,假如存在一些相同的配置信息,在对该配置信息进行修改时,我们需要一个一个进行修改,这样会大大增加维护的成本,不方便管理。这时如果使用一个专门放配置中心的组件,将相同的配置信息放在配置中心,需要的时候直接拉取,这样可以大大节约维护的成本, 而zookeeper即可实现配置中心的功能。
- 分布式锁
- 在多个用户访问同一台主机上的应用程序数据时,我们可以通过加锁解决并发操作的问题,但是如果有多台主机相同的应用程序要访问同一数据时,这个时候我们在一台主机上加锁是不能解决另一台主机的并发问题的,换句话说自己的锁只对自己有效并不影响别的 ,这个时候就需要分布式锁解决这类问题,我个人理解分布式锁像是从所有主机中抽取出来的一把锁,或者是有一把总锁对所有主机都有效。
- 集群管理
- zookeeper作为注册中心,管理服务提供方的ip地址端口号url信息,并在服务消费方请求需要时发送给服务消费方
Zookeeper命令操作
Zookeeper数据模型
- Zookeeper是一个树形目录服务,其数据模型和Unix的文件系统目录树很类似,拥有一个层次化结构
- 这里面的每一个节点都被称为:ZNode,每个节点都会保存自己的数据和节点信息
- 节点可以拥有子节点,同时也允许少量数据存储在该节点之下
- 节点分为四大类
- PRESISTENT:持久化节点
- EPHEMERAL:临时节点
- PRESISTENT_SEQUENTIAL:持久化顺序节点 -s
- EPHEMERAL_SEQUENTIAL:临时顺序节点 -es
Zookeeper客户端常用命令
-
连接ZooKeeper服务端
./zkCli.sh -server ip:port 如果是本机连接可以不写 ip:port -
断开连接:quit
-
CRUD
- 设置(修改)节点值:set 节点path value
- 查看命令帮助:help
- 删除单个节点:delete 节点path
- 显示指定目录下节点:ls 目录名
- 删除带有子节点的节点:deleteall 节点path
- 创建节点:create 节点path value
- 获取节点值:get 节点path
- 查看路径下节点信息及子节点:ls s 节点路径
- 查看节点状态:stat 节点path
Zookeeper客户端常用命令
- 启动ZooKeeper服务:./zkServer.sh start
- 查看ZooKeeper服务状态:./zkServer.sh status
- 停止ZooKeeper服务:/zkServer.sh stop
- 重启ZooKeeper.服务:/zkServer.sh restart
Zookeeper JavaAPI操作
Curator介绍
- Curator是Apache ZooKeeper的Java客户端库
- 常见的ZooKeeper Java API:
- 原生Java API
- ZkClient
- Curator
- Curator项目的目标是简化ZooKeeper客户端的使用
Curator API常用操作
- 连接zk server服务器
@Before
public void before() {
//参数1 --> zk server 服务ip地址:端口号
//参数2 --> 会话超时时间
//参数3 --> 连接超时时间
//参数4 --> 序列化方式
zkClient = new ZkClient("ip:端口号", 60000 * 30, 60000, new SerializableSerializer());
}
private ZkClient zkClient;
@Test
public void test01(){
System.out.println(zkClient);
}
@After
public void after() {
zkClient.close();
}
- 创建节点
@Test
public void test() {
//创建持久节点
zkClient.create("/app2", "q", CreateMode.PERSISTENT);
//创建持久顺序节点
zkClient.create("/app3","w", CreateMode.PERSISTENT_SEQUENTIAL);
//创建临时节点
zkClient.create("/app4","e", CreateMode.EPHEMERAL);
//创建临时顺序节点
zkClient.create("/app5","r",CreateMode.EPHEMERAL_SEQUENTIAL);
}
- 查看某个节点信息
@Test
public void test2(){
//查看某个节点数据,需要保证创建和获取的数据序列化方式相同,如果是在linux上创建的节点那么在Java上获取就会出现序列化不匹配的异常
Object o = zkClient.readData("/app2");
System.out.println(o);
//查看节点状态
Stat stat = new Stat();
System.out.println(o);
//查看节点创建时间
System.out.println(stat.getCtime());
//查看版本
System.out.println(stat.getCversion());
//查看id
System.out.println(stat.getCzxid());
}
- 修改节点信息
zkClient.writeData("/app2","qq");
- 查看当前节点所有子节点
@Test
public void test4(){
List<String> children = zkClient.getChildren("/");
for(String c : children ){
System.out.println(c);
}
}
- 监听
@Test
public void test5() throws IOException {
zkClient.subscribeDataChanges("/app1", new IZkDataListener() {
//当前节点数据变化时触发
@Override
public void handleDataChange(String dataPath, Object o) throws Exception {
System.out.println("当前节点路径"+dataPath);
System.out.println("当前节点信息"+o);
}
//当前节点删除时触发
@Override
public void handleDataDeleted(String dataPath) throws Exception {
System.out.println("当前节点路径"+dataPath);
}
});
//阻塞当前监听
System.in.read();
}
分布式锁
- 在我们进行单机应用开发,涉及并发同步的时候,我们往往采用synchronized或者Lock的方式来解决多线程间的代码同步问题,这时多线程的运行都是在同一个VM之下,没有任何问题。
-
但当我们的应用是分布式集群工作的情况下,属于多VM下的工作环境,跨VM之间已经无法通过多线程的锁解决同步问题。
-
那么就需要一种更加高级的锁机制,来处理种跨机器的进程之间的数据同步问题,这就是分布式锁
- 分布式锁实现
- 分布式锁原理
核心思想:当客户端要获取锁,则创建节点,使用完锁,则删除该节点。
1.客户端获取锁时,在lock节点下创建临时顺序节点。
2.然后获取lock下面的所有子节点,客户端获取到所有的子节点之后,如果发现自己创建的子节点序号最小,那么就认为该客户端获取到了锁。使用完锁后,将该节点删除。
3.如果发现自己创建的节点并非lock所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,同时对其注册事件监听器,监听删除事件。
4.如果发现比自己小的那个节点被删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是lock子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。
Zookeeper集群
- Leader选举:
- Serverid:服务器ID
- 比如有三台服务器,编号分别是1,2,3。编号越大在选择算法中的权重越大。
- Zxid:数据ID
- 服务器中存放的最大数据ID.值越大说明数据越新,在选举算法中数据越新权重越大
- 在Leader选举的过程中,如果某台ZooKeeper获得了超过半数的选票,则此ZooKeeper就可以成为Leader了。
- Serverid:服务器ID
搭建
- 搭建要求
真实的集群是需要部署在不同的服务器上的,但是在我们测试时同时启动很多个虚拟机内存会吃不消,所以我们通常会搭建伪集群,也就是把所有的服务都搭建在一台虚拟机上,用端口进行区分。我们这里要求搭建一个三个节点的Zookeeper集群(伪集群)。
- 配置集群
-
在每个zookeeper的data目录下创建一个myid文件,内容分别是1、2、3。这个文件就是记录每个服务器的ID
echo 1 >/usr/local/zookeeper-cluster/zookeeper-1/data/myid
echo 2 >/usr/local/zookeeper-cluster/zookeeper-2/data/myid
echo 3 >/usr/local/zookeeper-cluster/zookeeper-3/data/myid -
在每一个zookeeper的zoo.cfg配置客户端访问端口(clientPort)和集群服务器IP列表。
集群服务器IP列表如下
vim /usr/local/zookeeper-cluster/zookeeper-1/conf/zoo.cfg
vim /usr/local/zookeeper-cluster/zookeeper-2/conf/zoo.cfg
vim /usr/local/zookeeper-cluster/zookeeper-3/conf/zoo.cfgserver.1=192.168.149.135:2881:3881
server.2=192.168.149.135:2882:3882
server.3=192.168.149.135:2883:3883
- 启动集群:分别启动每个实例
/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh start
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh start
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh start
启动后查询一下每个实例的运行状态
/usr/local/zookeeper-cluster/zookeeper-1/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-2/bin/zkServer.sh status
/usr/local/zookeeper-cluster/zookeeper-3/bin/zkServer.sh status