Zookeeper

    xiaoxiao2022-07-04  114

    在有curator这个框架之前的时候,然后zookeeper怎么去实现分布式锁,就是利用刚才的那种方案,就是可以去实现, 你会有各种各样的疑问,你刚才讲的设计是得先get一下,你不如直接create,因为创建性能还不是那么高的,其实你有各种各样的疑问 你得去了解他,你要知道怎么做才能快,你要为性能考虑才能那样去做,之前这块也说了,我们往上走一走吧,很多介绍的东西, 你当时有一个大体的概念,但是没有一个很详细的概念,刚才在这里为什么那么去说,就是他的数据基本上是存在内存中的, 就是他的一些视图模型,单一视图,都是在内存中的,所以说,在JMater百分百读的场景下,并发可以达到这个,12到13万, 你想想吧,所以get的性能是很高的,我一个client端,只要网络没有太大的问题,没有延迟的情况下,get一下马上就能得到反馈, 这个效率是非常高的,所以之前我先get一下有没有,你不能get都不get就直接create,等着那块就抛异常了,等着报异常就不好, 一般我们不那么去做,就是让性能能更好,都是先去get一下,先去内存里去取,看有没有这条数据,然后再决定到底要不要create, 极少量的情况下会发生两个同时get,这个时候创建点肯定有一个先后顺序的,肯定是一个成功,一个失败的,这就是zookeeper怎么 去实现分布式这块

    然后再往下看,还是要看这个API,其实倒不如直接讲Curator框架,如果不讲这些东西,你直接拿去用的话,面试的时候是很吃亏的, zookeeper怎么去实现分布式锁的,你得讲明白是用临时节点,为什么啊,因为临时节点效率也高,第二点我什么要用get啊, 因为zookeeper是有这么一个特性,数据是存在内存中的,get的性能是非常高的,所以说我这么去做,是有理由有依据的, 然后咱们再去看一下API,这块要说的一点,咱们是不允许递归创建节点的,你父节点没有创建的情况下是不允许创建子节点的, 咱们可以试一试 我们先ls /一下

    我把testRoot删掉, rmr /testRoot

    然后ls /,当前只有一个zookeeper节点,不允许递归创建节点,父节点不存在的情况下是不允许创建节点的, 它是不允许支持序列化框架的,比如我们能不能做这么一件事,user的key就换一个主键ID,value就放一个user对象, 或者就一个JAVA对象,这个zookeeper是不支持的,以后它是不支持序列化的,你可以去使用一些第三方的序列化框架, 比如Hessian,Kryo序列号框架,就是这些序列号框架,Kryo框架的性能也是非常好的,包括dubbo中也是采用了一种方式, 第三个参数是一个权限节点,模模糊糊不知道是什么概念,Ids它是一个API,它是一个静态的,然后他有一个OPEN_ACL_UNSAFE, 开放权限即可,这个表示开放权限,后期有一些参数,基本上我从来没有关注过,包括很多API封装的时候,第三方框架就写死了, 一般在权限没有太高要求的情况下是没有必要关注的,你就不用费这个事去关注了,相关的问题我们通过网络,不会考虑太详细, 只不过是他提供的功能是这样,这个不用关注,然后最后一个是创建节点类型,有4种类型,有持久化的,有持久化顺序的, 有个先后顺序,当然会有个什么问题,你可能调一个方法,一个client端C1,现在10个节点,那你这10个节点要有顺序, 可能网络走到这个时候,第一个创建可能绕了一圈,第二个可能是没有先后顺序的,如果你有顺序的话你就可以去看看, 你这个zookeeper要做一个队列,你可以create一个sequential,就是你要那他做一个队列的一个事,其实很多技术可以提供一个解决 方案,就是Redis有一个list类型,它是有一些附加的功能,只不过这种实现起来会比较难,你要抓住最核心的作用是干什么的, 然后你辅助的功能,很简单的情况下你可以去用,我们不用消息队列,你想实现基于内存的,很多问题你自己可以去解决, 两种解决你可以自己去做,包括这个zookeeper也一样,他想实现一个队列,他可以去提供这个接口,一般有没有人这么去做呢, 这是我强调的一个问题,他不仅仅能实现一点,我只是大概提一下,然后往下走,还有一种是异步的方式,节点也分同步和异步, 同步的方式就是我创建完了之后,我发一个请求到zookeeper上,我这边得到一个返回值,这是同步的方式,我的代码肯定是在这一点 延迟,创建调API,同步方式,异步呢,代码是直接往下走,我又起了一个额外的线程,去监听zookeeper的返回,这是可以去实现的, 这里有一个异步方式

    package com.learn.zookeeper.base; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooKeeper; /** * 这就是关于zookeeper增删改查操作 * 简单的讲一下 * 里面有一些特殊的方法 * 每一套API * 无论是exist,delete,还是get * 都有两套 * 一种是同步的方式 * 一种是回调的方式 * 然后基本上去操作API呢 * 会有一些限制 * 比如说不能递归的去创建啊 * 不能递归的去删除 * 去掉getChildren方法的时候 * 十六进制JAVA怎么表示来着 * 有点不记得了 * 用byte[]字节数组 * get只能取得一层 * 你不能把下面的节点也取得到 * 有很多限制 * 这是关于咱们zookeeper的API * * @author Leon.Sun * */ public class ZookeeperBase { static final String CONNECT_ADDR = "59.110.138.145:2181"; // static final String CONNECT_ADDR = "192.168.80.88:2181,192.168.80.87:2181,192.168.80.86:2181"; static final int SESSION_OUTTIME = 2000;//ms /** 信号量,阻塞程序执行,用于等待zookeeper连接成功,发送成功信号 */ static final CountDownLatch connectedSemaphore = new CountDownLatch(1); public static void main(String[] args) throws Exception{ ZooKeeper zk = new ZooKeeper(CONNECT_ADDR, SESSION_OUTTIME, new Watcher(){ @Override public void process(WatchedEvent event) { KeeperState keeperState = event.getState(); EventType eventType = event.getType(); //如果是建立连接 if(KeeperState.SyncConnected == keeperState){ if(EventType.None == eventType){ try { System.out.println("开始连接....."); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("zk 建立连接"); //如果建立连接成功,则发送信号量,让后续阻塞程序向下执行 connectedSemaphore.countDown(); } } } }); //进行阻塞 connectedSemaphore.await(); System.out.println("执行啦.."); Thread.sleep(5000); //创建父节点 // String ret = zk.create("/testRoot", "testRoot11".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // System.out.println(ret); //创建子节点 /** * 现在我想又一次创建 * 肯定是没有/testRoot这个节点的 * 就像创建/children * 行吗 * 肯定是不行的 * 咱们运行一下吧 * KeeperException$NoNodeException: KeeperErrorCode = NoNode for /testRoot/children * 就是你没有父节点你就想创建子节点 * zookeeper原生的API是不支持的 * 怎么去实现的 * 你得先判断一下 * 先来个if再来个else * 其实Curator的框架怎么去封装的呢 * 无非就是先if判断当前 * 如果路径很长的话 * 还有个aaa * 还有个ccc * 一层一层的去判断 * 先判断children他的上一级有没有 * 再判断ccc他的上一级有没有 * aaa他的上一级有没有 * 其实就是这个意思 * 只要他的上一级没有我就抛一个异常 * 然后像Curator框架 * 如果不存在我就一层一层的创建 * 如果/testRoot这个不存在就创建出来 * 如果/aaa这个不存在就创建出来 * /ccc这个不存在我还得创建出来 * 他有个ifparentexist的方法 * 不允许去创建没有的父节点的 * 咱们把刚才的事说一下 * 把刚才的事说一下 * 不允许递归创建节点 * * */ // String ret = zk.create("/testRoot/aaa/ccc/children", "children data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); /** * 如果你这么去创建是不行的 * 咱们ls /一下 * 我现在根节点下只有一个zookeeper * 然后是没有testRoot的 * 创建它会返回一个什么值呢 * 它是抛了一个异常 * KeeperException$NoNodeException: KeeperErrorCode = NoNode for /testRoot/children * 在原生的zookeeper * 它是不允许你递归的去创建节点的 * 以后的Curator框架 * 会有一个ifparentexist * 它会帮你把父节点创建好 * 这也是原生API的问题 * 其实Curator也是把原生的API进行一个封装 * 无非是在判断节点的时候判断一下他上个节点是不是存在 * 如果不存在就把他创建好 * 如果存在的话就创建节点 * 这是第一个问题 * 在原生的API中是不允许递归创建节点的 */ // String ret = zk.create("/testRoot/children", "children data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); /** * create其实是有CallBack和Object的 * 就是每个方法都提供了两套API * 一个是正常的创建同步的创建 * 还有个是异步的创建 * 异步创建效率有点高 * 咱们同步的是一个 * 异步的去删除 * 创建成功了 * /testRoot * 然后我在下面ls / * 下面就有testRoot这个节点了 * 然后接下来进行delete * 这个Delete操作其实也很简单 * * 这里先注释 * 否则再创建就会报exists节点已经存在 * * /testRoot这个创建好了以后 * 我再去取的时候就有了 * dataVersion = 0 * 版本号的标识被改了几次 * update了几次 * * */ // String ret = zk.create("/testRoot", "testRoot11".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // System.out.println(ret); /** * 现在这样去做 * 我这里delete一个节点 * 其实它这个delte方法也有 * zookeeper能存数据的版本号 * 比如我们去get /testRoot * dataVersion = 0 * 你会发现这个版本号是0 * 这里有很多version * 但是我们就看dataVersion * 第一次是0 * 比如我修改一下 * set /testRoot 2222 * 我再去get的时候发现dataVersion改变了 * dataVersion = 1 * 如果我在改一下 * set /testRoot 5555 * dataVersion = 2 * 我能取到每一个版本号 * 对应的你要delete哪一个版本号的数据 * 你要是写-1就是全清空 * 就是全删除 * 一般我们去做delete节点的话 * 现在的testRoot的版本号已经到了2了 * * 现在我想去删除 * delete一般传了第二个参数-1 * 4个参数吗 * 然后这里有一个VoidCallback * Object ctx2可以传入一个Callback的参数 * 实例化出来VoidCallback这么一个对象 * 这个是一个静态的 * 它是异步的 * * */ // zk.delete("/testRoot", -1,new VoidCallback() { // // /** // * 然后你要去重载一个方法是这个processResult // * // * 这就是我之前说的一个callback方法 // * 每一个delete都会存在一个回调 // * // */ // @Override // public void processResult(int rc, String path, Object ctx2) { // /** // * 我休眠1秒钟 // */ // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } // /** // * 第一个int类型的东西是啥啊 // * 响应码 // * 如果是0表示回调成功 // * 如果是-4表示端口连接 // * 如果是-110表示指定节点存在 // * -112表示会话已经过期 // * callback可能成功可能失败 // * 能有好多个状态 // */ // System.out.println(rc); // /** // * path表示节点的参数路径是什么 // * // * 第二个参数是path // * 当前删除的节点是什么 // */ // System.out.println(path); // /** // * 然后还有一些东西 // * ctx上下文环境 // * 还有一个name // * 很多回调的参数 // * 我现在只取了这三个 // * // */ // System.out.println(ctx2); // } // // /** // * 这个a是一个Object // * 就是传入任何一个参数 // * 都会回调到ctx2这里 // * 咱们之前学RocketMQ的事务 // * 事务的callback里面也会有一个参数 // * 其实一个道理 // * 这是一个callback的一个机制 // * -1是跳过版本检查 // * 如果不是当前的版本就不删除了 // * 我数据库可能有一份记录 // * 当前咱两是不是一个版本号 // * 如果是的话我做一个check // */ // },"a"); System.out.println("继续执行......."); /** * 在这里等待这么长的时间 */ // Thread.sleep(5000); /** * 我现在直接做一个删除 * 成功了以后我们再去get * get /testRoot * Node does not exist: /testRoot * 一般不考虑版本号 * 一般用-1 * */ // zk.delete("/testRoot", 2); /** * 其实你也不用休眠了 */ // Thread.sleep(10000); //获取节点洗信息 /** * 接下来看获得节点 * ZooKeeper.getData(String path, boolean watch, Stat stat) * watch先不用理会 * stat状态呢也不用理会 * 因为如果你要讲API的话 * 但是不用关注后面两个参数 * 其实就是想去获得节点的value值 * value值返回的还是一个data类型 * testRoot咱们包装成一个String * 基本上就是这个操作 * 先讲简单的再讲复杂的 * 咱们create /testRoot 1122 * 创建成功以后ls / * 看到testRoot了 * 咱们现在执行一下看有没有效果 * 后面两个参数我们后面会讲 * 先不用关注这么复杂的东西 * * */ // byte[] data = zk.getData("/testRoot", false, null); // /** // * 现在我想获得一堆怎么办 // * // */ System.out.println(new String(data)); // // /** // * 一堆children我想获得的话 // * // */ System.out.println(zk.getChildren("/testRoot", false)); // // /** // * 他返回的是一个String类型的list // * ZooKeeper.getChildren(String path, boolean watch) // * 第二个参数是watch // * 总是有一个watch的东西 // * 这个东西其实很烦 // * create /testRoot/a1 1111 // * create /testRoot/a2 2222 // * create /testRoot/a5 2222 // * 现在ls /testRoot下面 // * [a1, a2, a5] // * 你会发现有三个节点三个孩子了 // * 他相当于一个父亲了 // * 然后我现在getChildren的时候 // * 把孩子都给拿出来了 // * 这是很简单的 // * 返回的类型已经固定了 // * // */ // List<String> list = zk.getChildren("/testRoot", false); // // /** // * 我就去循环他 // * // */ // for (String path : list) { // /** // * 这个path是一个相对的路径 // * a1,a2,a3 // * 如果你想取值的话 // * 你得加上前面的父路径/testRoot // * 然后a1,a2,a3 // * 他取出来的是一个相对的 // * 我就用之前取的方法 // * // * 我这块就是这样去做的 // * 我直接循环path // * 看path有没有就行了 // * 你会发现这是没有的 // * 就是getChildren方法你想做递归呢 // * 他是只支持直接子节点 // * 能理解我说的意思吧 // * 这是肯定的 // * 原生API就是这么去实现的 // * 为什么这么去实现呢 // * 其实也是有他的道理的 // * 这是关于getChildren方法的 // * // * // */ // System.out.println(path); // String realPath = "/testRoot/" + path; // /** // * realPath这个路径传进去 // * 得到的结果就是1111,2222,2222这个了 // * 这个应该很简单 // * 暂时不用去理会这个事 // * 先把这个简单的API讲完 // * 再加一层就不行了 // * 你试一下 // * 我在a1下面还有一层b1 // * a1下面有一个b1 // * create /testRoot/a1/b1 11 // * 咱们可能就是这种情况了 // * 我现在能不能把b1也取出来呢 // * // * // */ // System.out.println(new String(zk.getData(realPath, false, null))); // } /** * 修改节点其实也很简单 * 修改节点 * testRoot原先叫什么名字咱们看一下 * get /testRoot * 原先叫1122 * 我想给他改成"modify data root"这个 * -1就是不检查版本号了 * 不管你版本号这个问题了 * 修改完了再去取 * 就变成modify data root这个了 * get /testRoot * 数据就变成modify data root这个了 * 这个就相当于update * * */ // zk.setData("/testRoot", "modify data root".getBytes(), -1); // byte[] data = zk.getData("/testRoot", false, null); // System.out.println(new String(data)); //修改节点的值 // zk.setData("/testRoot", "modify data root".getBytes(), -1); // byte[] data = zk.getData("/testRoot", false, null); // System.out.println(new String(data)); //判断节点是否存在 /** * exists你可以先判断节点是否已经存在 * 比如a2 * 打印一下 * 它会返回一个布尔类型 * 4127,4127,1558515470535,1558515470535,0,0,0,0,4,0,4127 * 这个长的字符串不用理会 * 就跟cZxid = 0x101b这个有点像了 * 来试一下吧 * 每次都一样 * 我不知道行不行 * * 如果你给一个a5 * */ // System.out.println(zk.exists("/testRoot/a1", false)); /** * 如果你给一个a8 * 我在运行的话 * 返回的就是一个Null * null就证明这个节点不存在 * 如何去判断是否存在 * 现在也把exist这个方法测试OK了 * 然后最后还有一个delete * 这个delete就不用说了 * 就是想删除一个节点 * 既然不支持递归的创建 * * get /testRoot * 我现在查的是a2 * 那我还得这么去做 * get /testRoot/a2 * cZxid = 0x1035 * a2是这个值也应该存在 * * * */ System.out.println(zk.exists("/testRoot/a2", false)); long k = 0x1035; System.out.println(k); // 删除节点 /** * 原生的API只有两个方法 * 基本上你找不到递归删除的方法 * * */ // zk.delete("/testRoot/a2", -1); /** * 删除这个就报异常了 * KeeperException$NotEmptyException: KeeperErrorCode = Directory not empty for /testRoot * Directory not empty * 这个Directory不是空的 * 下面有孩子你就删不了了 * 既不支持递归的创建 * 也不支持递归的删除 * 能理解这个意思吗 * 这是最基本的API操作 * 让你去了解zookeeper最基本的操作 * * */ // zk.delete("/testRoot", -1); // System.out.println(zk.exists("/testRoot/a2", false)); zk.close(); } }

     

    最新回复(0)