Zookeeper

打印 上一主题 下一主题

主题 916|帖子 916|积分 2748

zookeeper

ZooKeeper是一个开源的分布式应用程序协调服务
简单来说可以理解为zookeeper = 文件系统+监听通知机制
应用场景:

  • 集群管理、服务器状态感知
  • 分布式应用配置管理
  • 统一命名服务
  • 分布式锁
小总结:

  • 为客户提供写数据功能  数据不大 状态信息数据
  • 为客户提供读取据功能
  • 为用户提监控通知功能  节点数据的变化   节点的子节点个数
Zookeeper的高度可靠性
是一个分布式的系统,多个节点  并且节点中记录的数据是完全一致(一致性) , 当某个zk的节点宕机之后不会影响工作。因为Zookeeper的主节点不存在单点故障!Zookeeper的主节点是可以动态选举出来的!
Zookeeper的选举机制(奇数台)
zookeeper的进程在不同的工作模式下,有不同的通信端口(比如选举时,通过端口3888通信;作为leader或者follower接收客户端请求时通过端口2181;leader和follower之间通信用2888)
zk集群安装的时候 会人为的为每台机器分配一个唯一的id
Leader选举过程(以3个节点的集群为例):
•        集群初次启动时的选举流程

  • 第一台机器(id=1)启动,发现没有leader,进入投票模式,投自己,并收到自己投这一票,得1票,不能当选leader(当leader的条件是,集群机器数量过半的票数)
  • 第2台机器(id=2)启动,发现没有leader,进入投票模式,投自己(因为自己的id>1 收到的另一台机器的票的id)
  • 第1台机器收到2的票,发现集群中有一个比自己id大的机器上线了,重新投票,投id=2
  • 第2台收到的得票数为2票,过半数,自己当选,切换模式:Leader模式
  • 第1台就发现有Leader存在了,自己切换模式:Follower
  • 第3台启动,发现有Leader,自动进入Follower状态
如果每个节点是同时启动的zk  同时选举自己 ,同时广播  , 同时获取别人的广播,3号机器会当选
•        集群在运行过程中的选举流程

  • 在某个时间点上,id=2机器挂了(leader),别的机器发现没有leader了,全体进入投票模式
  • 先投自己,票中会携带(自己的id,自己的数据的版本号)
  • 大家都投数据版本最新的节点做leader,如果有多个节点数据版本一样,则从中选id最大的那个作为投票目标!
从上述投票机制可以看出:
Zookeeper集群的节点数最好配置为奇数!
Zookeeper集群的节点规模一般在3~5台就够!
数据结构

zookeeper数据模型的结构与Unix文件系统很类似,整体上可看作是一棵树(也类似linux,hdfs),每一个节点称作一个ZNode。每一个ZNode默认只能够存储1MB的数据(无法存储海量数据)。每个ZNode都可以通过其路径唯一标识。
安装

1.上传安装包并解压
2.修改配置文件
  1. cd /opt/apps/zookeeper-3.4.6/conf
  2. # 将 zoo_sample.cfg的文件名修改成zoo.cfg
  3. mv  zoo_sample.cfg   zoo.cfg
  4. # 进入到zoo.cfg中修改配置信息
  5. vi zoo.cfg
  6. # 修改两个地方:
  7. # 1.数据存储路径
  8. dataDir=/opt/apps/zookeeper-3.4.6/zkData
  9. # 2.zk服务地址
  10. # Set to "0" to disable auto purge feature
  11. #autopurge.purgeInterval=1
  12. server.1=linux01:2888:3888
  13. server.2=linux02:2888:3888
  14. server.3=linux03:2888:3888
复制代码
3.在各个节点上,手动创建数据存储目录
  1. mkdir -p /opt/apps/zookeeper-3.4.6/zkData
复制代码
4.在各个节点的数据存储目录中,生成一个myid文件,内容为它的id
  1. # linux01
  2. echo 1 > /opt/apps/zookeeper-3.4.6/zkData/myid
  3. # lunux02
  4. echo 2 > /opt/apps/zookeeper-3.4.6/zkData/myid
  5. # lunux03
  6. echo 3 > /opt/apps/zookeeper-3.4.6/zkData/myid
  7. # 注意:在linux02上,myid里面的值就是2,03上面的值就是3,是依次递增的
复制代码
5.分发安装包
  1. for i in 2 3
  2. do
  3. scp -r zookeeper-3.4.6 linux0$i:$PWD
  4. done
复制代码
6.启动zk集群脚本
  1. #!/bin/bash
  2. for i in 1 2 3
  3. do
  4. ssh linux0${i} "source /etc/profile;/opt/apps/zookeeper-3.4.6/bin/zkServer.sh $1"
  5. done
  6. sleep 2
  7. if [ $1 == start ]
  8. then
  9. for i in {1..3}
  10. do
  11. ssh doit0${i} "source /etc/profile;/opt/apps/zookeeper-3.4.6/bin/zkServer.sh status "
  12. done
  13. fi
复制代码
命令行客户端操作

客户端启动连接
  1. bin/zkCli.sh  本地连接
  2. bin/zkCli.sh  -server doit02:2181  连接到指定的服务节点
复制代码
ls:查看某个路径下的key
  1. ls /
  2. [doit, zookeeper]
复制代码
ls2:查看指定目录下的节点  详细
  1. ls2 /zookeeper
  2. [quota]
  3. cZxid = 0x0                              该数据节点被创建时的事务id
  4. ctime = Thu Jan 01 08:00:00 CST 1970     该数据节点创建时间
  5. mZxid = 0x0                              该数据节点被修改时最新的事物id
  6. mtime = Thu Jan 01 08:00:00 CST 1970     该数据节点最后修改时间
  7. pZxid = 0x0                              当前节点的父级节点事务ID
  8. cversion = -1                            子节点版本号(子节点修改次数,每修改一次值+1)
  9. dataVersion = 0                          当前节点版本号(可以理解为修改次数,每修改一次值+1)
  10. aclVersion = 0                           当前节点acl版本号(acl节点被修改次数,每修改一次值+1)
  11. ephemeralOwner = 0x0                     临时节点标示,当前节点如果是临时节点,则存储的创建者的会话id(sessionId),如果不是,那么值=0
  12. dataLength = 0                           当前节点数据长度
  13. numChildren = 1                          当前节点子节点个数
复制代码
Create:创建一个znode
  1. create /aaa ddd
  2. [zk: localhost:2181(CONNECTED) 1] create /doit niubi
  3. # 创建完成后当get的时候会有如下信息
  4. [zk: localhost:2181(CONNECTED) 33] get /doit
  5. niubi
  6. cZxid = 0x2000000013
  7. ctime = Fri May 05 22:25:28 CST 2023
  8. mZxid = 0x2000000013
  9. mtime = Fri May 05 22:25:28 CST 2023
  10. pZxid = 0x2000000013
  11. cversion = 0
  12. dataVersion = 0
  13. aclVersion = 0
  14. ephemeralOwner = 0x0
  15. dataLength = 5
  16. numChildren = 0
  17. # 可以看到cversion 子节点的版本号还是0,以及dataVersion数据节点版本也还是0
  18. #由于目前是默认创建的(没有使用-s -e),所以创建的这个节点是非顺序的,没有顺序的,并且是持久化的
  19. #如果想要创建一个临时节点,可以加上 -e 这个参数
  20. [zk: localhost:2181(CONNECTED) 36] create -e /doit/abc aaa
  21. Created /doit/abc
  22. [zk: localhost:2181(CONNECTED) 37] stat /doit            
  23. cZxid = 0x2000000013
  24. ctime = Fri May 05 22:25:28 CST 2023
  25. mZxid = 0x2000000013
  26. mtime = Fri May 05 22:25:28 CST 2023
  27. pZxid = 0x2000000015
  28. cversion = 1
  29. dataVersion = 0
  30. aclVersion = 0
  31. ephemeralOwner = 0x0
  32. dataLength = 5
  33. numChildren = 1
  34. #用stat命令查看的时候会发现 cversion 变成了1
  35. # 在去查看临时节点的时候
  36. [zk: localhost:2181(CONNECTED) 38] stat /doit/abc
  37. cZxid = 0x2000000015
  38. ctime = Fri May 05 22:28:29 CST 2023
  39. mZxid = 0x2000000015
  40. mtime = Fri May 05 22:28:29 CST 2023
  41. pZxid = 0x2000000015
  42. cversion = 0
  43. dataVersion = 0
  44. aclVersion = 0
  45. ephemeralOwner = 0x187ec3969a40000
  46. dataLength = 3
  47. numChildren = 0
  48. #会发现临时节点和持久化节点的ephemeralOwner 是不一样的
  49. #那么应该如何删除临时节点呢?只要断开客户端的链接即可。按住ctrl + c进行退出,然后再查询一下子节点
  50. #创建顺序节点 create -s
  51. [zk: localhost:2181(CONNECTED) 39] create -s /doit/aaa abc
  52. Created /doit/aaa0000000001
  53. [zk: localhost:2181(CONNECTED) 40] create -s /doit/aaa abc
  54. Created /doit/aaa0000000002
  55. [zk: localhost:2181(CONNECTED) 41] create -s /doit/aaa abc
  56. Created /doit/aaa0000000003
  57. [zk: localhost:2181(CONNECTED) 42] create -s /doit/aaa abc
  58. Created /doit/aaa0000000004
  59. [zk: localhost:2181(CONNECTED) 43] create -s /doit/aaa abc
  60. Created /doit/aaa0000000005
  61. #可以看到,用-s创建出来的子节点后面会跟上一个递增的序号
  62. [zk: localhost:2181(CONNECTED) 44] ls /doit
  63. [aaa0000000004, aaa0000000003, abc, aaa0000000005, aaa0000000002, aaa0000000001]
复制代码
get:查看一个key的value
  1. get /paopao
  2. niubi
  3. cZxid = 0x2000000003
  4. ctime = Fri May 05 22:10:47 CST 2023
  5. mZxid = 0x2000000005
  6. mtime = Fri May 05 22:12:23 CST 2023
  7. pZxid = 0x2000000003
  8. cversion = 0
  9. dataVersion = 2
  10. aclVersion = 0
  11. ephemeralOwner = 0x0
  12. dataLength = 5
  13. numChildren = 0
复制代码
set:给key的value赋值
  1. set /doit paopao
  2. cZxid = 0x2000000013
  3. ctime = Fri May 05 22:25:28 CST 2023
  4. mZxid = 0x200000001b
  5. mtime = Fri May 05 22:38:04 CST 2023
  6. pZxid = 0x200000001a
  7. cversion = 6
  8. dataVersion = 1
  9. aclVersion = 0
  10. ephemeralOwner = 0x0
  11. dataLength = 5
  12. numChildren = 6
复制代码
delete:删除空节点
  1. delete /paopao
  2. # 如果这个节点不是空的,delete是删除不了的,只能用rmr删除
  3. create /paopao aaa
  4. Created /paopao
  5. create /doit/abc  aaa
  6. Created /doit/abc
  7. delete /paopao
  8. Node not empty: /paopao
  9. # rmr:删除节点(可以递归删除)
  10. rmr /paopao
复制代码
事件监听

zookeeper中对znode的变化描述有3种事件类型:

  • 节点value变化事件
  • 节点的子节点变化事件
  • 节点被创建、被删除事件
对应的,客户端向zk注册监听的命令为:
  1. get    /abc watch
  2. ls     /abc watch
  3. ls2    /abc watch
  4. stat   /abc watch
  5. # 第一个客户端
  6. get /paopao  watch  # 获取节点数据的时候监控(监控当前节点数据的变化)
  7. # 克隆一个会话 在客户端修改节点数据的值
  8. set /paopao bbb
  9. # 第一个客户端会出现如下信息:
  10. WatchedEvent state:SyncConnected type:NodeDataChanged path:/paopao
  11. # 第一个客户端
  12. ls /teache  watch
  13. # 克隆一个会话 创建一个客户端 , 在/teacher节点下创建一个新的节点/删除节点下的子节点
  14. create /doit/aaa aaa
  15. Created /doit/aaa
  16. # 第一个客户端会出现如下信息:
  17. WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/doit
  18. # 注意:客户端向zk注册的事件监听,只会被通知1次!如果需要持续监听,则需要反复注册
复制代码
java客户端

添加依赖
  1. <dependencies>
  2.     <dependency>
  3.         <groupId>org.apache.zookeeper</groupId>
  4.         <artifactId>zookeeper</artifactId>
  5.         <version>3.4.9</version>
  6.     </dependency>
  7. </dependencies>
复制代码
获取客户端
  1. import org.apache.zookeeper.KeeperException;
  2. import org.apache.zookeeper.ZooKeeper;
  3. import java.io.IOException;
  4. import java.util.List;
  5. public class zkCliDemo {
  6.     public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
  7.         String connectionString = "linux01:2181,linux02:2181,linux03:2181" ;
  8.         // 超时时间  单位毫秒
  9.         int sessionTimeOut = 2000 ;
  10.         // 参数三  监听  null
  11.         // alt+enter   导包  异常处理
  12.         // 快速返回值    .var   或者   ctrl+alt+v
  13.         // 获取操作ZK的java客户端对象
  14.         ZooKeeper zooKeeper = new ZooKeeper(connectionString, sessionTimeOut, null);
  15.         /**
  16.          * 获取指定目录下的所有的子节点
  17.          * 参数一  路径   一定以/开始
  18.          * 参数二 是否监控
  19.          */
  20.         List<String> list = zooKeeper.getChildren("/", null);
  21.         // 遍历指定节点下的所有的子节点
  22.         for (String s : list) {
  23.             System.out.println("/节点下的所有的子节点有: /"+s);  // "/"s
  24.         }
  25.         zooKeeper.close();
  26.     }
  27. }
  28. 根节点下有哪些子节点:cluster
  29. 根节点下有哪些子节点:brokers
  30. 根节点下有哪些子节点:zookeeper
  31. 根节点下有哪些子节点:admin
  32. 根节点下有哪些子节点:isr_change_notification
  33. 根节点下有哪些子节点:log_dir_event_notification
  34. 根节点下有哪些子节点:controller_epoch
  35. 根节点下有哪些子节点:doit
  36. 根节点下有哪些子节点:consumers
  37. 根节点下有哪些子节点:latest_producer_id_block
  38. 根节点下有哪些子节点:config
  39. 根节点下有哪些子节点:hbase
复制代码
创建子节点
  1. import org.apache.zookeeper.CreateMode;
  2. import org.apache.zookeeper.ZooDefs;
  3. import org.apache.zookeeper.ZooKeeper;
  4. import java.util.List;
  5. /**
  6. * 1.获取到zk的对象
  7. * 2.调用api
  8. */
  9. public class CreateDemo {
  10.     public static void main(String[] args) throws Exception {
  11.         //创建zk的对象
  12.         ZooKeeper zk = new ZooKeeper("linux01:2181",2000,null);
  13.         //创建一个子目录,返回创建的目录
  14.         /**
  15.          * 参数1:需要创建的节点名称,注意需要写绝对路径
  16.          * 参数2:该节点对应value的值,需要传二进制数组
  17.          * 参数3:该节点的权限,访问控制列表
  18.          * OPEN_ACL_UNSAFE:使用完全开放的ACL,允许客户端对znode进行读/写
  19.          * CREATOR_ALL_ACL:授予节点创建者所有权限。需要注意的是,设置此权限之前,创建者必须已经通了服务器的认证
  20.          * READ_ACL_UNSAFE:仅仅具有读权限
  21.          *参数四  节点的类型
  22.          *  EPHEMERAL临时的   session级别的临时  客户端断开以后 节点会被删除
  23.          *  EPHEMERA _SEQUENTIAL 临时有序
  24.          *  PERSISTENT   永久
  25.          *  PERSISTENT_SEQUENTIAL  永久有序
  26.          */
  27.         String s = zk.create("/teacher1", "xiaotao".getBytes(), ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT);
  28.         //打印返回创建的路径
  29.         System.out.println(s);
  30.         //关闭资源
  31.         zk.close();
  32.     }
  33. }
复制代码
删除子节点
  1. import org.apache.zookeeper.ZooKeeper;
  2. public class DeleteDemo {
  3.     public static void main(String[] args) throws Exception {
  4.         //创建zk的对象
  5.         ZooKeeper zk = new ZooKeeper("linux01:2181",2000,null);
  6.         /**
  7.          * 注意只能删除空节点
  8.          * -1代表任何版本,如果是4,那么到这个版本才能删掉,否则就会报错
  9.          * Exception in thread "main" org.apache.zookeeper.KeeperException$BadVersionException:
  10.          * KeeperErrorCode = BadVersion for /teacher
  11.          */
  12.         zk.delete("/teacher",4);
  13.         
  14.         //zk.delete("/teacher",-1);
  15.         
  16.         zk.close();
  17.     }
  18. }
复制代码
递归删除节点
  1. import org.apache.zookeeper.KeeperException;
  2. import org.apache.zookeeper.ZooKeeper;
  3. import java.util.List;
  4. /**
  5. * 递归删除文件夹
  6. */
  7. public class RMRDemo {
  8.     public static void main(String[] args) throws Exception {
  9.         //创建zk的对象
  10.         ZooKeeper zk = new ZooKeeper("linux01:2181",2000,null);
  11.         rmr("/teacher",zk);
  12.         zk.close();
  13.     }
  14.     public static void rmr(String path,ZooKeeper zk) throws Exception {
  15.         List<String> children = zk.getChildren(path, null);
  16.         if (children != null && children.size() >0){
  17.             for (String child : children) {
  18.                 rmr(path+"/"+child,zk);
  19.                 System.out.println("正在删除的子节点是:"+path+"/"+child);
  20.             }
  21.         }
  22.         zk.delete(path,-1)
  23.     }
  24. }
复制代码
获取节点的数据
  1. import org.apache.zookeeper.KeeperException;
  2. import org.apache.zookeeper.ZooKeeper;
  3. import java.io.IOException;
  4. import java.util.List;
  5. public class GetDemo {
  6.     public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
  7.         String connectionString = "linux01:2181,linux02:2181,linux03:2181" ;
  8.         // 超时时间  单位毫秒
  9.         int sessionTimeOut = 2000 ;
  10.         // 参数三  监听  null
  11.         // alt+enter   导包  异常处理
  12.         // 快速返回值    .var   或者   ctrl+alt+v
  13.         // 获取操作ZK的java客户端对象
  14.         ZooKeeper zk = new ZooKeeper(connectionString, sessionTimeOut, null);
  15.         byte[] data = zk.getData("/a0000000004", null, null);
  16.         String str = new String(data);
  17.         System.out.println(str);
  18.         zk.close();
  19.     }
  20. }
复制代码
修改节点的数据
  1. import org.apache.zookeeper.KeeperException;
  2. import org.apache.zookeeper.ZooKeeper;
  3. import org.apache.zookeeper.data.Stat;
  4. import java.io.IOException;
  5. public class SetDemo {
  6.     public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
  7.         String connectionString = "linux01:2181,linux02:2181,linux03:2181" ;
  8.         int sessionTimeOut = 2000 ;
  9.         ZooKeeper zk = new ZooKeeper(connectionString, sessionTimeOut, null);
  10.         /**
  11.          * 更新节点的数据
  12.          *  参数一  节点
  13.          *  参数二  value的值
  14.          *  参数三 数据版本
  15.          */
  16.         Stat stat = zk.setData("/a", "abc".getBytes(), -1);
  17.         zk.close();
  18.     }
  19. }
复制代码
监控节点中值得变化
  1. import org.apache.zookeeper.KeeperException;
  2. import org.apache.zookeeper.WatchedEvent;
  3. import org.apache.zookeeper.Watcher;
  4. import org.apache.zookeeper.ZooKeeper;
  5. import java.io.IOException;
  6. /**
  7. * 监听节点中值的变化
  8. */
  9. public class WatcherDemo {
  10.     public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
  11.         //创建zk的对象
  12.         ZooKeeper zk = new ZooKeeper("linux01:2181", 2000, new Watcher() {
  13.             @Override
  14.             public void process(WatchedEvent watchedEvent) {
  15.                 System.out.println("就单纯的整一个监听器");
  16.             }
  17.         });
  18.         byte[] data = zk.getData("/teacher", new Watcher() {
  19.             @Override
  20.             public void process(WatchedEvent watchedEvent) {
  21.                 byte[] data = new byte[0];
  22.                 try {
  23.                     data = zk.getData("/teacher", this, null);
  24.                 } catch (Exception e) {
  25.                     e.printStackTrace();
  26.                 }
  27.                 System.out.println(new String(data));
  28.             }
  29.         }, null);
  30. //        System.out.println(new String(data));
  31.         Thread.sleep(Integer.MAX_VALUE);
  32.     }
  33. }
复制代码
监听节点中子节点得个数变化
  1. import com.sun.media.jfxmediaimpl.HostUtils;
  2. import org.apache.zookeeper.KeeperException;
  3. import org.apache.zookeeper.WatchedEvent;
  4. import org.apache.zookeeper.Watcher;
  5. import org.apache.zookeeper.ZooKeeper;
  6. import java.io.IOException;
  7. import java.util.List;
  8. /**
  9. * 监听节点中值的变化
  10. */
  11. public class WatcherDemo1 {
  12.     public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
  13.         //创建zk的对象
  14.         ZooKeeper zk = new ZooKeeper("linux01:2181", 2000, new Watcher() {
  15.             @Override
  16.             public void process(WatchedEvent watchedEvent) {
  17.                 System.out.println("就单纯的整一个监听器");
  18.             }
  19.         });
  20.         List<String> list = zk.getChildren("/teacher", new Watcher() {
  21.             @Override
  22.             public void process(WatchedEvent watchedEvent) {
  23.                 try {
  24.                     List<String> children = zk.getChildren("/teacher", this);
  25.                     for (String child : children) {
  26.                         System.out.println(new String(zk.getData("/teacher/"+child,null,null)));
  27.                     }
  28.                     System.out.println("===========分隔符===============");
  29.                 } catch (Exception e) {
  30.                     e.printStackTrace();
  31.                 }
  32.             }
  33.         });
  34.         //遍历所有的节点
  35. //        System.out.println(list);
  36.         Thread.sleep(Integer.MAX_VALUE);
  37.     }
  38. }
  39. shell客户端:
  40. [zk: localhost:2181(CONNECTED) 68] create /teacher/a   1
  41. Created /teacher/a
  42. [zk: localhost:2181(CONNECTED) 69] create /teacher/b   2
  43. Created /teacher/b
  44. [zk: localhost:2181(CONNECTED) 70] create /teacher/c   3
  45. Created /teacher/c
  46. [zk: localhost:2181(CONNECTED) 71]
  47. 控制台:
  48. 就单纯的整一个监听器
  49. 1
  50. ===========分隔符===============
  51. 1
  52. 2
  53. ===========分隔符===============
  54. 1
  55. 2
  56. 3
  57. ===========分隔符===============
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

我可以不吃啊

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表