JAVAEE分布式技术之Zookeeper技术
Posted teayear
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVAEE分布式技术之Zookeeper技术相关的知识,希望对你有一定的参考价值。
1,课程回顾
2,本章重点
zk节点类型
zk的常用命令及ACL权限管理
java操作zk(java代码模拟集群管理)
3,具体内容
3.1 zk节点和节点类型
znode数据存储图的讲解
节点类型:
1、PERSISTENT–持久化目录节点
客户端与zookeeper断开连接后,该节点依旧存在
2、PERSISTENT_SEQUENTIAL-目持久化顺序编号录节点
客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
3、EPHEMERAL-临时目录节点
客户端与zookeeper断开连接后,该节点被删除,临时节点不可以创建子节点
4、EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点
客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
3.2 常用命令:
3.2.1 客户端链接:
保证集群启动
使用zkCli.sh默认链接本机
zkCli.sh
close 关闭会话
quit 退出 或者直接使用quit
或者使用 zkCli -server ip或者主机名:端口号 链接相应服务器
zkCli.sh -server 192.168.23.11:2181
zkCli.sh -server cluster3:2181
或者是在zk命令行: connect ip地址或者主机名称:端口号, 链接相应服务器
[zk: 192.168.23.12:2181(CONNECTED) 0] connect 192.168.170.42:2181
connect cluster3:2181
quit 退出
3.2.2 常用命令:
help 帮助命令,会显示所有zk的命令
ls 存储路径 查看路径下的信息(路径必须是绝对路径)
ls [-s] [-w] [-R] path
列出子节点 -s状态( State) -R 递归查看所有子节点(Recursive) -w 添加监听(watch)
ls / 查看根目录下的内容
ls -R / 递归查看根节点所有子节点
ls -w / 查看并监听根目录,如果根下子节点发生变化都会被监听到,但是只监听一次(提高zk效率)
在另外一个会话中执行:
create /frame1 ‘frame1’
create /frame2 ‘frame2’ 发现只有一次有监控信息
在原会话中执行
ls -w /
在另外一个会话中执行:
delete /frame2
delete /frame1 发现只有一次有监控信息
close 关闭与服务端的链接
close
quit 退出
create [-s] [-e] path [data] [acl]
-s 创建有序节点
-e 创建临时节点
data 节点内容
acl(access control list) 访问控制权限
创建持久节点
create /frame ‘frame’
create /frame/spring ‘spring’
ls /frame
close
quit
zkCli.sh 再次连接,发现会话ID发生变化,说明不上一次会话
ls /
ls /frame 断开会话后持久化节点依然存在
创建持久有序节点
create -s /frame/spring/ioc ‘ioc1’
create -s /frame/spring/ioc ‘ioc2’
create -s /frame/spring/ioc ‘ioc3’
ls /frame/spring 发现3个持久有序节点
quit 退出
zkCli.sh 再次连接,发现会话ID发生变化,说明不上一次会话
ls /frame/spring 发现3个持久有序节点依然存在
create /frame/spring/ioc0000000000/ioccontainer ‘iocc’ 在持久有序节点下还可以创建子节点
ls /frame/spring/ioc0000000000
总结: 只要持久节点,都可以有子节点
创建临时节点:create -e 路径 “字符串信息” (-e是创建短暂节点)
create -e /frame/spring/aop ‘aop’
ls -s /frame/spring/aop 发现 ephemeralOwner 就是依赖当前会话ID
ls -s /
ls -s /frame 发现持久节点 ephemeralOwner 都是0x0
create /frame/spring/aop/cglibproxy ‘cglibproxy’
create -e /frame/spring/aop/cglibproxy ‘cglibproxy’ 都错误 临时节点不能有子节点
创建 临时有序节点
create -e -s /frame/spring/mvc ‘mvc1’
create -e -s /frame/spring/mvc ‘mvc2’
create -e -s /frame/spring/mvc ‘mvc3’
ls /frame/spring
create -e -s /frame/spring/mvc0000000005/ds ‘DespatcherServlet’
ls -s /frame/spring/mvc0000000005 发现 ephemeralOwner 就是依赖当前会话ID
quit
zkCli.sh
ls /frame/spring/ 退出quit当前节点,再次链接zkCli.sh查看,发现临时节点不存在
总结: 只要是临时节点,都不可以有孩子
在zk1查看节点 get 路径 [watch] 获取数据时,启动监听,监听数据(只生效一次)
[zk: localhost:2181(CONNECTED) 18] get /aaa watch
在zk2在另一链接上:
[zk: localhost:2181(CONNECTED) 0] set /aaa 11
zk1截图:
get [-s] [-w] path
查看节点数据 -s 包含节点状态 -w 添加监听
get /frame/spring
get -s /frame/spring
set /frame/spring ‘spirng1’
get -s /frame/spring
set /frame/spring ‘spirng111’
get -s /frame/spring
create /frame/spring/boot ‘boot’
get -s /frame/spring
delete /frame/spring/boot
get -s /frame/spring
get -w /frame/spring 查看节点内容并监控 但是也是只监控一次
在另外一个会话中,改变/frame/spring的内容
set /frame/spring ‘spirng4’
set /frame/spring ‘spirng5’ 发现只有一次可以触发监控
zxid:节点创建时的zxid
ctime:节点创建时间
mZxid:节点最近一次更新时的zxid
mtime:节点最近一次更新的时间
pZxid: 表示该节点的子节点列表最后一次修改的事务ID,添加子节点或删除子节点就会影响子节点列表,但是修改子节点的数据内容则不影响该ID
cversion:子节点更新次数
dataVersion:本节点数据更新次数
aclVersion:节点ACL(授权信息)的更新次数
ephemeralOwner:如果该节点为临时节点,ephemeralOwner值表示与该节点绑定的session id. 如果该节点不是临时节点,ephemeralOwner值为0
dataLength:节点数据长度,本例中为hello world的长度
numChildren:子节点个数
set 更新节点内容:
set /frame/spring ‘spring1’ 更新节点内容
delete [-v version] path 删除单个节点
删除节点,(不能存在子节点)
delete /frame/spring/mvc0000000008
create /frame/spring/boot ‘boot’
create /frame/spring/boot/sarun ‘SpringApplicationrun’
delete /frame/spring/boot 错误
deleteall path
删除路径及所有子节点
deleteall /frame/spring/boot
stat查看节点的状态
stat /frame/spring
setquota增加配额
setquota -n|-b val path
-n 设置子节点的配额数量
-b 设置节点内容的长度
setquota -n 3 /frame 为/frame路径设置子节点数量限制 包含自己在内,最多3个子节点
listquota /frame 查看配额
ls /frame 查看子节点
在另外一个会话中打开日志:
tail -f /usr/zookeeper/logs/zookeeper-root-server-cluster1.out
create /frame/mybatis ‘ORM’
create /frame/hibernate ‘ORM’ 第二次创建目录时就会提示
listquota查询配额
listquota path
-1 表示无限, 即没有限制
listquota /frame
delquota删除配额
delquota [-n|-b] path
delquota /frame 删除所有配额
3.2.3 ACL权限控制:
ACL:Access Control List 访问控制列表
相关命令:
getAcl 获取某个节点的acl权限信息
setAcl 设置某个节点的acl权限信息
addauth 输入认证授权信息,注册时输入明文密码(登录),但是在zk的系统里,密码是以加密后的形式存在的
特性:
ZooKeeper的权限控制是基于每个znode节点的,需要对每个节点设置权限
每个znode支持设置多种权限控制方案和多个权限
子节点不会继承父节点的权限,客户端无权访问某节点,但可能可以访问它的子节点
授权格式:
授权策略:授权对象: 权限
scheme: id: Permission
Scheme:(计划)授权的策略 包含下面:
world:默认方式,相当于全部都能访问 代表所有人
ip:使用客户端的主机IP作为ACL ID 。这个ACL表达式的格式为addr/bits ,此时addr中的有效位与客户端addr中的有效位进行比对。
auth:使用已添加认证的用户认证(cli中可以通过addauth digest user:pwd 来添加当前上下文中的授权用户)
digest:即用户名:密码这种方式认证,这也是业务系统中最常用的。用 username:password 字符串来产生一个MD5串,然后该串被用来作为ACL ID。认证是通过明文发送username:password 来进行的,当用在ACL时,表达式为username:base64 ,base64是password的SHA1摘要的编码。
ID:授权的对象
权限赋予的用户或者一个实体,例如:IP 地址 或者是用户(授权) 或者是 anyone
Permission:授予的权限 CRWDA
zookeeper支持的权限
CREATE©: 创建权限,可以在在当前node下创建child node
READ®: 读权限,可以获内容及子节点
WRITE(w): 写权限,可以向当前node写数据
DELETE(d): 删除权限,可以删除当前的child nodes
ADMIN(a): 管理权限,可以设置当前node的permission
测试:
world: getAcl /aaa //获取原来权限列表
setAcl /aaa world:anyone:cda //设置新的访问权限列表
getAcl /aaa //再次查看
get /aaa //没有r时,不可以查看节点内容
setAcl /aaa world:anyone:crwda //设置为原来的
get /aaa //又可以查看
ip: create /a1 ‘a1’ //创建新节点
setAcl /a1 ip:127.0.0.1:crwda
getAcl /a1
get /a1 //获取不到数据
quit //退出
zkCli.sh -server 127.0.0.1:2181 //重新登录
get /a1 //获取到数据
setAcl /a1 ip:192.168.23.91:cdwra,ip:192.168.23.92:cdwra
getAcl /a1
quit
zkCli.sh -server 192.168.23.91:2181 //重新登录,限制只能是本机登录
get /a1 //可以获取
zkCli.sh -server 192.168.23.93:2181 //在93登录
get /a1 //不可以
auth: create /a2 ‘a2’
getAcl /a2
addauth digest scott:tiger #增加授权用户,明文用户名和密码
setAcl /a2 auth:scott:cdwra #授予权限
getAcl /a2 //设置时为明码,查看时,加密过的
get /a2 可以查看
重新链接后,需要再次添加授权用户才可以查看:
addauth digest admin:123 // 设置错误的认证信息
get /a2 // 不可以查看
addauth digest scott:tiger //设置正确的认证信息
get /a2 //可以
digest: create /a3 ‘a3’
getAcl /a3
setAcl /a3 digest:user:6DY5WhzOfGsWQ1XFuIyzxkpwdPo=:crwda #授权
getAcl /a3
rmr /a3 #直接删除没权限
addauth digest user:123456 #增加认证用户
rmr /a3 // 成功
3.3 zookeeper java api
使用下列地址查找依赖包:
https://mvnrepository.com/
1,创建项目,引入jar包
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
<!--日志 start-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
<!--日志end-->
2,增删改查
config.properties
#链接字符串 C:\\Windows\\System32\\drivers\\etc\\hosts 和/etc/hosts
connectString=zk1:2181,zk2:2181,zk3:2181,zk4:2181,zk5:2181
#链接超时时长
sessionTimeout=2000
```java
package com.aaa.util;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Properties;
/**
* fileName:ZookeeperUtil
* description:
* author:zz
* createTime:2020/5/16 9:34
* version:1.0.0
*/
public class ZookeeperUtil
private Properties properties;
//各个方法共享
private ZooKeeper zooKeeper;
/**
* 空构造,初始化配置文件
*/
public ZookeeperUtil()
InputStream inputStream = null;
try
// Properties 是Map子孙类
properties = new Properties();
//使用任意类的Class对象中提供的getResourceAsStream读取配置文件为 输入字节流
inputStream =ZookeeperUtil.class.getResourceAsStream("/config.properties");
//加载内容
properties.load(inputStream);
catch (IOException e)
e.printStackTrace();
finally
try
if(inputStream!=null)
inputStream.close();
catch (IOException e)
e.printStackTrace();
/**
* 初始化zk
*/
public void initZk()
try
zooKeeper = new ZooKeeper(properties.getProperty("connectString"),
Integer.valueOf(properties.getProperty("sessionTimeout")), new Watcher()
@Override
public void process(WatchedEvent watchedEvent)
System.out.println("当查看子节点或者查看节点数据时,使用watch时,在这回调");
);
catch (IOException e)
e.printStackTrace();
/**
* 节点的创建
* @param path 节点路径
* @param nodeData 节点数据
* @param type 节点类型 type=0 持久节点 type=1 临时节点 type=2 持久有序 type=3 临时有序
* @return
*/
public boolean createZKNode(String path,String nodeData,int type)
//String path, byte[] data, List<ACL> acl, CreateMode createMode
try
// nodeData.getBytes("utf-8") 支持中文的
zooKeeper.create(path,nodeData.getBytes("utf-8"),
ZooDefs.Ids.OPEN_ACL_UNSAFE,// world:anyone:crwda
CreateMode.fromFlag(type) //type=0 持久节点 type=1 临时节点 type=2 持久有序 type=3 临时有序
);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 根据节点路径查询子节点
* @param path 节点路径
* @param isWatch 是否监控
* @return
*/
public List<String> getChirdrenByPath(String path,boolean isWatch)
try
//查询并返回
return zooKeeper.getChildren(path, isWatch);
catch (Exception e)
e.printStackTrace();
return null;
/**
* 根据节点路径删除节点
* @param path
* @return
*/
public boolean deleteNodeByPath(String path)
try
// version=-1 删除所有版本
zooKeeper.delete(path,-1);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 获取节点数据
* @param path
* @param isWatch
* @return
*/
public String getNodeData(String path,boolean isWatch)
try
byte[] byteArray = zooKeeper.getData(path, isWatch, null);
return new String(byteArray);
catch (KeeperException e)
e.printStackTrace();
catch (InterruptedException e)
e.printStackTrace();
return null;
/**
* 节点数据的更新
* @param path
* @return
*/
public boolean updateNodeData(String path,String nodeData)
try
zooKeeper.setData(path,nodeData.getBytes("utf-8"),-1);
return true;
catch (Exception e)
e.printStackTrace();
return false;
/**
* 判断节点是否存在
* @param path
* @return
*/
public boolean isNodeExist(String path)
try
Stat exists = zooKeeper.exists(path, false);
if(exists!=null)
return true;
catch (Exception e)
e.printStackTrace();
return false;
ZookeeperUtil
ZKUtilTest
package com.aaa.test;
import com.aaa.util.ZookeeperUtil;
import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
/**
* fileName:ZKUtilTest
* description:
* author:zz
* createTime:2020/5/16 11:13
* version:1.0.0
*/
public class ZKUtilTest
private ZookeeperUtil zookeeperUtil;
/**
* 执行Test之前执行的方法
*/
@Before
public void init()
//加载配置读取配置
zookeeperUtil = new ZookeeperUtil();
//使用配置连接zk集群
zookeeperUtil.initZk();
/**
* 测试创建节点方法
*/
@Test
public void testCreateZKNode()
boolean result = zookeeperUtil.createZKNode("/testCreateNode1/cnode", "子节点3", 2);
if(result)
System.out.println("操作成功");
else
System.out.println("操作失败");
/**
* 测试获取子节点
*/
@Test
public void testGetChirdrenByPath()
List<String> chirdren = zookeeperUtil.getChirdrenByPath("/testCreateNode1", false);
System.out.println("子节点有:");
for (String s : chirdren)
System.out.println(s);
/**
* 测试获取接单数据
*/
@Test
public void testGetNodeData()
String nodeData = zookeeperUtil.getNodeData("/testCreateNode1", false);
System.out.println(nodeData);
/**
* 测试更新数据
*/
@Test
public void testUpdateNodeData()
//测试节点是否存在
if(zookeeperUtil.isNodeExist("/testCreateNode1"))
boolean result = zookeeperUtil.updateNodeData("/testCreateNode1", "根节点。。。");
if(result)
System.out.println("操作成功");
else
System.out.println("操作失败");
/**
* 测试删除节点
*/
@Test
public void testDeleteNodeByPath()
List<String> chirdren = zookeeperUtil.getChirdrenByPath("/testCreateNode1", false);
//判断是否有子节点
if(chirdren!=null&&chirdren.size()>0)
//循环删除子节点
for (String c : chirdren)
zookeeperUtil.deleteNodeByPath("/testCreateNode1/"+c);
//在删除父节点
boolean result = zookeeperUtil.deleteNodeByPath("/testCreateNode1");
if(result)
System.out.println("操作成功");
else
System.out.println("操作失败");
@After
public void distory()
System.out.println("最终执行的方法。。。");
回顾:
zk数据存储 树状,节点类型4中:
持久节点,持久有序节点,临时节点,临时有序节点
命令:
help 帮助命令(查看所有命令)
connect ip(或者主机名称):端口(2181)
close 关闭当前的会话
quit 退出
create [-s] [-e] /路径 内容 创建节点,默认就是持久节点,根据参数不同,创建不同节点
set /路径 内容 修改节点内容
get /路径 [watch] 获取节点内容[是否监听(只监听一次)]
getAcl /路径 ACL (访问控制列表,对每个节点进行设置,不会级联子节点)
默认: world:anyone:crwda
setAcl /路径 schema:id:permission
schema: world ip auth digest
id: anyone ip 自定义用户(scott,admin)
permission: create 是否创建子节点
read 获取当前节点内容
write 修改当前节点内容
delete 删除当前节点内容
admin 是否可以修改ACL访问控制列表
delete /路径 只能删除没有子节点的节点
rmr /路径 可以删除所有节点(有字节点也可以)
ls /路径 [watch] 查看子节点 [可以使用监听,子节点发生变化(添加和删除),只监听一次]
histroy 返回最近使用的10条命令
java 对zk的操作(和命令操作类似,只不过换成了 java代码)
3.4 java模拟zk管理其他集群
(练习zookeeper java 方法操作)
1,分析原理
2,开发代码
util类:
package com.aaa.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* fileName:LoadP
* description:
* author:zz
* createTime:2020/7/14 21:11
* version:1.0.0
*/
public class LoadP
/**
* 加载配置文件方法
* @param path
* @return
*/
public static Properties load(String path)
Properties properties = new Properties();
InputStream inputStream = LoadP.class.getResourceAsStream("/"+path);
try
properties.load(inputStream);
catch (IOException e)
e.printStackTrace();
return properties;
服务注册:
package com.aaa.test.file;
import com.aaa.test.LoadP;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.util.Properties;
/**
* fileName:HdfsServer
* description:
* author:zz
* version:1.0.0
*/
public class HdfsServer
private ZooKeeper zooKeeper;
// private static final String parentPath="/servers";
private Properties load = LoadP.load("zk.properties");
/**
* 获取连接
* @throws Exception
*/
public void getZKConnect() throws Exception
zooKeeper = new ZooKeeper(load.getProperty("cstr"), Integer.valueOf(load.getProperty("to")), new Watcher()
public void process(WatchedEvent watchedEvent)
);
/**
* 注册
* @param serverName
* @throws KeeperException
* @throws InterruptedException
*/
public void registerToZkCluster(String serverName) throws KeeperException, InterruptedException
Stat stat = zooKeeper.exists(load.getProperty("parentPath"), false);
if(stat==null)
String parentPath = zooKeeper.create(load.getProperty("parentPath"), serverName.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("无父节点,创建:"+parentPath);
String s = zooKeeper.create(load.getProperty("parentPath") + "/hdfs", serverName.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(serverName+"主机上线了,注册节点为"+s);
/**
* 处理业务
* @param serverName
* @throws InterruptedException
*/
public void handleService(String serverName) throws InterruptedException
System.out.println(serverName+"开始工作,准备存储或者下载文件。。。");
Thread.sleep(Integer.MAX_VALUE);
public static void main(String[] args) throws Exception
HdfsServer hdfsServer =new HdfsServer();
hdfsServer.getZKConnect();
//args[0]="cs1";
hdfsServer.registerToZkCluster(args[0]);
hdfsServer.handleService(args[0]);
web服务感知:
package com.aaa.test.file;
import com.aaa.test.LoadP;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* fileName:WebServer
* description:
* author:zz
* version:1.0.0
*/
public class WebServer
private ZooKeeper zooKeeper;
private Properties load = LoadP.load("zk.properties");
/**
* 获取连接
* @throws Exception
*/
public void getZKConnect() throws Exception
zooKeeper = new ZooKeeper(load.getProperty("cstr"), Integer.valueOf(load.getProperty("to")), new Watcher<以上是关于JAVAEE分布式技术之Zookeeper技术的主要内容,如果未能解决你的问题,请参考以下文章