zookeeper是一个开源的分布式协调服务,是由雅虎创建的,基于google chubby。
1,zookeeper是什么:
分布式数据一致性解决方案
2,zookeeper能做什么:
数据的发布/订阅(配置中心:disconf) 、 负载均衡(dubbo利用了zookeeper机制实现负载均衡) 、命名服务、
master选举(kafka、hadoop、hbase)、分布式队列、分布式锁
3,zookeeper的特性:
单机环境安装:
1,下载zookeeper的安装包
http://apache.fayea.com/zookeeper/stable/zookeeper-3.4.10.tar.gz
2,解压zookeeper
tar -zxvf zookeeper-3.4.10.tar.gz
3,cd 到 ZK_HOME/conf , copy一份zoo.cfg
cp zoo_sample.cfg zoo.cfg
4,启动zookeeper
cd到bin目录下, zkServer.sh start
{start|start-foreground|stop|restart|status|upgrade|print-cmd}
5,zookeeper客户端
zkCli.sh
集群环境安装:
zookeeper集群, 包含三种角色: leader / follower /observer
observer
observer 是一种特殊的zookeeper节点。可以帮助解决zookeeper的扩展性(如果大量客户端访问我们zookeeper集群,需要增加zookeeper集群机器数量。从而增加zookeeper集群的性能。 导致zookeeper写性能下降, zookeeper的数据变更需要半数以上服务器投票通过。造成网络消耗增加投票成本)
1,observer不参与投票。 只接收投票结果。
2,不属于zookeeper的关键部位
既然要进行集群开发,那么本次将在三台主机上进行 ZooKeeper 的服务配置。这三台主机的列表如下:
编辑 每台主机上复制出来的zoo.cfg 文件:vim /usr/local/zookeeper/conf/zoo.cfg :
在每台主机zoo.cfg文件底部添加如下信息:
server.1=192.168.122.131:2888:3888
server.2=192.168.122.138:2888:3888
server.3=192.168.122.139:2888:3888
在 zoo.cfg 文件里面会存在有如下的几项配置信息:
· “tickTime=2000”:心跳的间隔时间,每 2 秒中要发送一个心跳,保证主机存在;
.“initLimit=10” : follower节点启动后与leader节点完成数据同步的时间;
.“syncLimit=5“: leader节点和follower节点进行心跳检测的最大延时时间 ;
.”dataLogDir“: 表示配置 zookeeper事务日志的存储路径,默认指定在dataDir目录下 ;
· “dataDir=/usr/data/zookeeper”:描述的是 ZooKeeper 的工作目录,里面就只有一个信息文件;
· “clientPort=2181”:客户端的连接端口;
· “server.x=主机名称:监听端口:选举端口”:定义所以存活的 ZooKeeper 进程的主机列表;
|- 每一台主机都有一个编号,而这个编号的文件一定要保存在工作目录中;
|- 监听端口:2888;
|- 投票端口:3888。
创建myid
在每一个服务器的dataDir目录下创建一个myid的文件,文件就一行数据,数据内容是每台机器对应的server ID的数字
如果要想启动 ZooKeeper 操作集群,主要使用的命令:/usr/local/zookeeper/bin/zkServer.sh
启动时候的顺序没有关系,反正会自动进行投票处理。
1、 随意找到任意一台主机,观察 ZooKeeper 的状态:zkServer.sh status
这个时候实际上会返回两类信息:leader(选举的领导)、follower(跟随者)。
2、 找到“leader”对应的主机信息,而后进行 ZooKeeper 服务的停止:zkServer.sh stop。
3、 也可以通过 jps 查看 ZooKeeper 相关信息:QuorumPeerMain
数据模型
zookeeper的数据模型和文件系统类似,每一个节点称为:znode. 是zookeeper中的最小数据单元。每一个znode上都可以
保存数据和挂载子节点。 从而构成一个层次化的属性结构
节点特性
持久化节点 : 节点创建后会一直存在zookeeper服务器上,直到主动删除
持久化有序节点 :每个节点都会为它的一级子节点维护一个顺序
临时节点 : 临时节点的生命周期和客户端的会话保持一致。当客户端会话失效,该节点自动清理
临时有序节点 : 在临时节点上多勒一个顺序性特性
Watcher
zookeeper提供了分布式数据发布/订阅,zookeeper允许客户端向服务器注册一个watcher监听。当服务器端的节点触发指定事件的时候会触发watcher。服务端会向客户端发送一个事件通知watcher的通知是一次性,一旦触发一次通知后,该watcher就失效。
ACL
zookeeper提供控制节点访问权限的功能,用于有效的保证zookeeper中数据的安全性。避免误操作而导致系统出现重大事故。
四:zookeeper常用的命令操作
1, create [-s] [-e] path data acl
-s 表示节点是否有序
-e 表示是否为临时节点
默认情况下,是持久化节点
2,get path [watch]
获得指定 path的信息
3.set path data [version]
修改节点 path对应的data
乐观锁的概念
数据库里面有一个 version 字段去控制数据行的版本号
4.delete path [version]
删除节点
stat信息
cversion = 0 子节点的版本号
aclVersion = 0 表示acl的版本号,修改节点权限
dataVersion = 1 表示的是当前节点数据的版本号
czxid 节点被创建时的事务ID
mzxid 节点最后一次被更新的事务ID
pzxid 当前节点下的子节点最后一次被修改时的事务ID
1,导入jar包
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.8version>
dependency>
2,demo
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class ApiOperatorDemo implements Watcher{
private final static String CONNECTSTRING="192.168.122.131:2181,192.168.122.138:2181," +
"192.168.122.139:2181";
private static CountDownLatch countDownLatch=new CountDownLatch(1);
private static ZooKeeper zookeeper;
private static Stat stat=new Stat();
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
zookeeper=new ZooKeeper(CONNECTSTRING, 5000, new ApiOperatorDemo());
countDownLatch.await();
ACL acl=new ACL(ZooDefs.Perms.ALL,new Id("ip","192.168.122.131"));
List acls=new ArrayList<>();
acls.add(acl);
zookeeper.exists("/authTest",true);
zookeeper.create("/authTest","111".getBytes(),acls,CreateMode.PERSISTENT);
System.in.read();
// zookeeper.getData("/authTest",true,new Stat());
/* System.out.println(zookeeper.getState());
//创建节点
String result=zookeeper.create("/node1","123".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
zookeeper.getData("/node1",new ZkClientApiOperatorDemo(),stat); //增加一个
System.out.println("创建成功:"+result);
//修改数据
zookeeper.setData("/node1","mic123".getBytes(),-1);
Thread.sleep(2000);
//修改数据
zookeeper.setData("/node1","mic234".getBytes(),-1);
Thread.sleep(2000);
*//* //删除节点
zookeeper.delete("/mic/mic1",-1);
Thread.sleep(2000);*//*
//创建节点和子节点
String path="/node11";
zookeeper.create(path,"123".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
TimeUnit.SECONDS.sleep(1);
Stat stat=zookeeper.exists(path+"/node1",true);
if(stat==null){//表示节点不存在
zookeeper.create(path+"/node1","123".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
TimeUnit.SECONDS.sleep(1);
}
//修改子路径
zookeeper.setData(path+"/node1","mic123".getBytes(),-1);
TimeUnit.SECONDS.sleep(1);*/
//获取指定节点下的子节点
/* List childrens=zookeeper.getChildren("/node",true);
System.out.println(childrens);*/
}
public void process(WatchedEvent watchedEvent) {
//如果当前的连接状态是连接成功的,那么通过计数器去控制
if(watchedEvent.getState()==Event.KeeperState.SyncConnected){
if(Event.EventType.None==watchedEvent.getType()&&null==watchedEvent.getPath()){
countDownLatch.countDown();
System.out.println(watchedEvent.getState()+"-->"+watchedEvent.getType());
}else if(watchedEvent.getType()== Event.EventType.NodeDataChanged){
try {
System.out.println("数据变更触发路径:"+watchedEvent.getPath()+"->改变后的值:"+
zookeeper.getData(watchedEvent.getPath(),true,stat));
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else if(watchedEvent.getType()== Event.EventType.NodeChildrenChanged){//子节点的数据变化会触发
try {
System.out.println("子节点数据变更路径:"+watchedEvent.getPath()+"->节点的值:"+
zookeeper.getData(watchedEvent.getPath(),true,stat));
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else if(watchedEvent.getType()== Event.EventType.NodeCreated){//创建子节点的时候会触发
try {
System.out.println("节点创建路径:"+watchedEvent.getPath()+"->节点的值:"+
zookeeper.getData(watchedEvent.getPath(),true,stat));
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else if(watchedEvent.getType()== Event.EventType.NodeDeleted){//子节点删除会触发
System.out.println("节点删除路径:"+watchedEvent.getPath());
}
System.out.println(watchedEvent.getType());
}
}
}