1 ,使用 idea 创建 maven 项目 :
双击打开 idea - create new Project - maven - next
- 填写 GAV ( G:com.itcast ; A:zkTest ;) - next - finish
2 ,引入 maven 依赖 :
<project xmlns&#61;"http://maven.apache.org/POM/4.0.0"xmlns:xsi&#61;"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation&#61;"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0modelVersion><groupId>com.itcastgroupId><artifactId>zkTestartifactId><version>1.0-SNAPSHOTversion><repositories><repository><id>clouderaid><url>https://repository.cloudera.com/artifactory/cloudera-repos/url>repository>repositories><dependencies><dependency><groupId>org.apache.zookeepergroupId><artifactId>zookeeperartifactId><version>3.4.5-cdh5.14.0version>dependency><dependency><groupId>junitgroupId><artifactId>junitartifactId><version>4.11version>dependency>dependencies>
project>
3 &#xff0c;建类 &#xff1a;Day01
右键 java - new - 输入 com.heima.zkTest.Day01 - OK
4 &#xff0c;看到 Day01 这个类 &#xff1a;
5 &#xff0c;查 – 子节点 &#xff1a; ls /
&#64;Test
public void ls() throws IOException, KeeperException, InterruptedException {ZooKeeper zk &#61; new ZooKeeper("node01:2181",5000,null);List<String> list &#61; zk.getChildren("/", null);for (String s : list) {System.out.println(s);}zk.close();
}
6 &#xff0c;查 – 数据 &#xff1a; get /xyj
&#64;Test
public void getXyj() throws IOException, InterruptedException, KeeperException {ZooKeeper zk &#61; new ZooKeeper("node01:2181",5000,null);byte[] data &#61; zk.getData("/xyj", null, null);String str &#61; new String(data);System.out.println(str);zk.close();
}
7 &#xff0c;查 – 元数据信息 &#xff1a;
&#64;Test
public void getStat() throws IOException, InterruptedException, KeeperException {ZooKeeper zk &#61; new ZooKeeper("node01:2181",5000,null);Stat stat &#61; new Stat();zk.getData("/xyj", null, stat);System.out.println("数据版本号&#xff1a;" &#43; stat.getVersion());System.out.println("节点创建时候的事务 id &#xff1a;" &#43;stat.getCversion());System.out.println("数据长度&#xff1a;" &#43; stat.getDataLength());zk.close();
}
8 &#xff0c;增 &#xff1a; create /a tom
&#64;Test
public void create() throws InterruptedException, IOException, KeeperException {ZooKeeper zk &#61; new ZooKeeper("node01:2181",5000,null);if(zk.exists("/a",null)&#61;&#61;null){String msg&#61;zk.create("/a","tom".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);System.out.println("创建成功&#xff1a;"&#43;msg);}else{System.out.println("节点已经存在&#xff0c;不要重复创建...");}zk.close();
}
9 &#xff0c;改 &#xff1a; set /a jerry
&#64;Test
public void setA() throws IOException, InterruptedException, KeeperException {ZooKeeper zk &#61; new ZooKeeper("node01:2181",5000,null);zk.setData("/a","jerry".getBytes(),-1);zk.close();
}
10&#xff0c;删 – 单节点&#xff1a; delete /a
&#64;Test
public void delete() throws IOException, KeeperException, InterruptedException {ZooKeeper zk &#61; new ZooKeeper("node01:2181",5000,null);zk.delete("/a",-1);zk.close();
}
11&#xff0c;删 – 目录 &#xff1a;
javaAPI 不提供删除目录的操作
12&#xff0c;理论 1 – 版本号 &#xff1a; 元数据中的版本号
- 修改和删除都需要用到版本号。
- 数据的初始版本号是 0
- 每次修改数据&#xff0c;版本号就会 &#43;1
- 当使用 javaAPI 修改&#xff0c;或这删除数据的时候&#xff0c;需要写版本号&#xff0c;如果写不对版本号&#xff0c;就无法操作数据。
- -1 代表最新版本号&#xff0c;我也不管你现在是什么版本&#xff0c;我就操作你这个数据的最新版本。
- 版本号的用途 &#xff1a;并发修改。
13&#xff0c;高级操作 &#xff1a;并发修改 ( 重点 )
- 先查出来版本号&#xff0c;然后再修改
public static void main(String[] args) throws Exception {ZooKeeper zk &#61; new ZooKeeper("node01:2181",5000,null);Stat stat &#61; new Stat();zk.getData("/a",null,stat);int version &#61; stat.getVersion();zk.setData("/a","aa".getBytes(),version);zk.close();
}
- 修改 12 次&#xff0c;依次修改 &#xff1a;
public static void main(String[] args) throws Exception {for (int i &#61; 1; i <&#61;12 ; i&#43;&#43;) {ZooKeeper zk &#61; new ZooKeeper("node01:2181",5000,null);Stat stat &#61; new Stat();zk.getData("/a",null,stat);int version &#61; stat.getVersion();zk.setData("/a","aa".getBytes(),version);zk.close();}
}
- 图示&#xff1a;每次修改操作都依次执行&#xff0c;前一次修改结束了&#xff0c;后一次修改才开始进行
- 并发修改 &#xff1a;12 个线程一起修改
结果 &#xff1a;11 个错误。
结论 &#xff1a;1 个用户修改成功&#xff0c;其他 11 个用户全部失败。
public static void main(String[] args) throws IOException {for (int i&#61;0;i<12;i&#43;&#43;){Thread t &#61; new Thread(new Runnable() {public void run() {try {ZooKeeper zk &#61; new ZooKeeper("node01:2181",5000,null);Stat stat &#61; new Stat();zk.getData("/a",null,stat);int version &#61; stat.getVersion();zk.setData("/a","aa".getBytes(),version);zk.close();} catch (Exception e) {e.printStackTrace();}}});t.start();}
}
- 12个线程&#xff0c;分批次修改&#xff0c;每三个线程一组&#xff1a;
public static void main(String[] args) throws Exception {for (int i &#61; 1; i <&#61;12 ; i&#43;&#43;) {final int j&#61;i;if(i&#61;&#61;4||i&#61;&#61;7||i&#61;&#61;10){Thread.sleep(1000);}Thread t &#61; new Thread(new Runnable() {public void run() {try {ZooKeeper zk &#61; new ZooKeeper("node01:2181", 5000, null);Stat stat &#61; new Stat();zk.getData("/a", null, stat);int version &#61; stat.getVersion();zk.setData("/a", "aa".getBytes(), version);System.out.println(j);zk.close();} catch (Exception e1) {e1.printStackTrace();}}});t.start();}
}
- 对同一时刻的理解 &#xff1a;版本图
1 &#xff0c;用户 1,2,3 就相当于同一时刻在修改
2 &#xff0c;在 1,2,3 发出修改操作时&#xff0c;他们看到的版本号都是 0
3 &#xff0c;但是 1 率先开始修改操作
4 &#xff0c;当 1 号修改完成后&#xff0c;数据的版本号已经变成 1 版本
5 &#xff0c;2号&#xff0c;3号&#xff0c;手中拿着的版本号仍然是 0 &#xff0c;但是数据版本号已经是 1 &#xff0c;所以 2,3 修改失败。
6 &#xff0c;组内不确定性&#xff1a;每个小组中都有一个跑得快的线程&#xff0c;占据主导地位&#xff0c;另外两个跑得慢&#xff0c;至于小组中的哪一个跑得快&#xff0c;是不确定的。
7 &#xff0c;时间不确定性&#xff1a;理论上说&#xff0c;线程之间存在竞争激烈的竞争&#xff0c;每个小组会有一个线程获胜&#xff0c;但是有可能修改操作执行的很快&#xff0c;以至于&#xff0c;它执行完后&#xff0c;下一个线程才开始启动&#xff0c;这样的话&#xff0c;一个小组中就有可能修改两次数据。
- 版本号的作用 &#xff1a;
锁死数据&#xff0c;有了版本号的存在&#xff0c;同一个时刻&#xff0c;只能有一个线程在操作 zookeeper 数据&#xff0c;其他的线程全部操作失败&#xff0c;从而达到支持并发修改操作的目的。 - 版本号 -1 的作用 &#xff1a;
1 &#xff0c;忽略其他版本号&#xff0c;修改最新版本。
2 &#xff0c;解锁数据&#xff0c;我不管别的线程在干嘛&#xff0c;我就要修改&#xff0c;说什么都不听&#xff0c;我就要改。 - 用版本号 -1 做修改操作 &#xff1a;12 个线程全成功&#xff0c;不报错
public static void main(String[] args) throws Exception {for (int i &#61; 1; i <&#61;12 ; i&#43;&#43;) {final int j&#61;i;Thread t &#61; new Thread(new Runnable() {public void run() {try {ZooKeeper zk &#61; new ZooKeeper("node01:2181", 5000, null);zk.setData("/a", "aa".getBytes(), -1);System.out.println(j);zk.close();} catch (Exception e1) {e1.printStackTrace();}}});t.start();}
}
10&#xff0c;结论 &#xff1a;
修改操作&#xff0c;单线程操作的话&#xff0c;就用 -1 版本号。
如果考虑并发安全问题&#xff0c;就要用到自己查询的版本号。
14&#xff0c;理论 2 – 权限 &#xff1a; 创建节点时用到 ( 谁可以访问这个节点 )
&#64;Test public void create() throws IOException, InterruptedException, KeeperException {ZooKeeper zk &#61; new ZooKeeper("node01:2181",5000,null);zk.create("/b","bb".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);zk.close();}
- OPEN_ACL_UNSAFE &#xff1a;开放权限&#xff0c;所有人都可读&#xff0c;可写
- CREATOR_ALL_ACL &#xff1a; 只有创建这个节点的用户&#xff0c;才能访问这个节点
- READ_ACL_UNSAFE &#xff1a;所有用户都可以读这个节点
15&#xff0c;理论 3 – 节点类型 &#xff1a; 创建节点时用到
- PERSISTENT &#xff1a;永久节点&#xff0c;创建后就一直存在&#xff0c;zk 重启&#xff0c;节点还在
- EPHEMERAL &#xff1a;临时节点&#xff0c;客户端断开后&#xff0c;节点消失 ( 打断点&#xff0c;试试看 )
- PERSISTENT_SEQUENTIAL &#xff1a;永久序列节点
- EPHEMERAL_SEQUENTIAL &#xff1a;临时序列节点
16 &#xff0c;临时节点测试 &#xff1a; 打断点
- 创建临时节点&#xff0c;打断点 &#xff0c;执行 &#xff1a;
- 查看这个节点 &#xff1a;看到临时节点
- 放开 debug &#xff1a;
- 再次查看 &#xff1a;临时节点消失
- 作用 &#xff1a;保存临时数据
17 &#xff0c;序列节点 &#xff1a; 就是一串数字
- 创建后&#xff0c;他会在节点名字后面加一串数字&#xff0c;10 个数字。
- 再次创建序列节点&#xff0c;又一串数字&#xff0c;并且这个数字是刚才的数字加一。
- 重要用途 &#xff1a;同步&#xff0c;锁。
- 类型&#xff1a;永久型序列节点&#xff0c;临时型序列节点。
- 看图&#xff0c;一看便知 &#xff1a;