分布式协调服务-Zookeeper

一:初步认识zookeeper

zookeeper是一个开源的分布式协调服务,是由雅虎创建的,基于google chubby。

1,zookeeper是什么:
分布式数据一致性解决方案

2,zookeeper能做什么:
数据的发布/订阅(配置中心:disconf) 、 负载均衡(dubbo利用了zookeeper机制实现负载均衡) 、命名服务、
master选举(kafka、hadoop、hbase)、分布式队列、分布式锁

3,zookeeper的特性:

  • 顺序一致性:从同一个客户端发起的事物请求,最终会严格按照顺序被应用daozookeeper中。
  • 原子性:所有的事务请求的处理结果在整个集群中的所有机器上的应用情况是一致的,也就是说,要么整个集群中的所有机器都成功应用了某一事务、要么全都不应用
  • 可靠性:一旦服务器成功应用了某一个事务数据,并且对客户端做了响应,那么这个数据在整个集群中一定是同步并且保留下来的
  • 实时性:一旦一个事务被成功应用,客户端就能够立即从服务器端读取到事务变更后的最新数据状态;(zookeeper仅仅保证在一定时间内,近实时)

二: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的一些概念

数据模型
zookeeper的数据模型和文件系统类似,每一个节点称为:znode. 是zookeeper中的最小数据单元。每一个znode上都可以
保存数据和挂载子节点。 从而构成一个层次化的属性结构

节点特性
持久化节点 : 节点创建后会一直存在zookeeper服务器上,直到主动删除

持久化有序节点 :每个节点都会为它的一级子节点维护一个顺序

临时节点 : 临时节点的生命周期和客户端的会话保持一致。当客户端会话失效,该节点自动清理

临时有序节点 : 在临时节点上多勒一个顺序性特性

Watcher
zookeeper提供了分布式数据发布/订阅,zookeeper允许客户端向服务器注册一个watcher监听。当服务器端的节点触发指定事件的时候会触发watcher。服务端会向客户端发送一个事件通知watcher的通知是一次性,一旦触发一次通知后,该watcher就失效。

ACL
zookeeper提供控制节点访问权限的功能,用于有效的保证zookeeper中数据的安全性。避免误操作而导致系统出现重大事故。

四:zookeeper常用的命令操作

1create [-s] [-e] path data acl
-s 表示节点是否有序
-e 表示是否为临时节点
默认情况下,是持久化节点

2get 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

五:JAVA中API的使用

1,导入jar包

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.8</version>
</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<ACL> 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<String> 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());
        }

    }
}

猜你喜欢

转载自blog.csdn.net/shuxing520/article/details/79988167