Zookeeper笔记

Zookeeper

概念

Zookeeper从设计模式角度来理解,是一个基于观察者模式设计的分布式服务管理框架

Zookeeper = 文件系统 + 通知机制

负责存储和管理大家都关心的数据,然后接收观察者的注册,ZK负责通知在ZK上注册的那些观察者做出相应的反应

特点

  • 由一个leader和多个follwer组成
  • 集群中只要有半数以上的节点存活就能正常服务
  • 全局数据一致,无论Client连接到那个server,数据都一样
  • 更新请求顺序进行
  • 数据更新原子性(一次数据更新要么成功要么失败)
  • 实时性
  • ZK中数据保存的格式为树状结构,没有文件的概念,节点下直接存的就是内容

如果不是第一次搭集群,需要把ZK安装目录下的zkData目录和logs删除

组成

节点

分为持久型和短暂型,序号型和不带序号型

持久化目录节点:客户端与ZK断开连接后,节点依然存在

持久化顺序编号目录节点:客户端与ZK断开连接后,节点依然存在,且节点名称含有顺序编号

临时目录节点:客户端与ZK断开连接后,节点被删除

临时顺序编号目录节点:客户端与ZK断开连接后,节点被删除,且节点名称含有顺序编号

Stat结构体

(1)czxid-创建节点的事务zxid

每次修改ZooKeeper状态都会收到一个zxid形式的时间戳,也就是ZooKeeper事务ID。

事务ID是ZooKeeper中所有修改总的次序。每个修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前发生。

(2)ctime - znode被创建的毫秒数(从1970年开始)

(3)mzxid - znode最后更新的事务zxid

(4)mtime - znode最后修改的毫秒数(从1970年开始)

(5)pZxid-znode最后更新的子节点zxid

(6)cversion - znode子节点变化号,znode子节点修改次数

(7)dataversion - znode数据变化号

(8)aclVersion - znode访问控制列表的变化号

(9)ephemeralOwner- 如果是临时节点,这个是znode拥有者的session id。如果不是临时节点则是0。

(10)dataLength- znode的数据长度

(11)numChildren - znode子节点数量

选举机制

半数存活原则表明ZK适合奇数台机器

选举机制总原则:集群中的每台机器都参与投票,通过交换选票得到每台机器的最终得票,一旦出现得票数超过机器总数一半以上数量,当前机器即为leader。

以5台机器为例,集群的机器顺时启动,当前集群中没有任何数据。
①. server1 启动,首先server1给自己投一票,然后看当前票数是否超过半数,结果没有超过,这时候leader就没选出来,当前选举状态是Locking状态。
②. server2 启动,首先server2先给自己投一票,因为当前集群已经有两台机器已启动,所以server1,server2会交换选票,交换后发现各自有一票,接下来比较myid 发现server2的myid值 > server2的myid值。此时server2胜出,最后server2有两票。最后再看当前票数是否半,发现未过半,集群的选举状态集训保持locking状态。
③. server3启动, 首先自己投自己一票,server1和server2也会投自己一票,然后交换选票发现都一样,接着比较myid 最后server3胜出,此时server3就有3票,同时server3的票数超过半数。所以server3成为leader。
④. server4启动,发现当前集群已经有leader 它自己自动成为follower
⑤. server5启动,发现当前集群已经有leader 它自己自动成为follower

以5台机器为例,当前集群正在使用(有数据/没数据),leader突然宕机的情况。
当集群中的leader挂掉,集群会重新选出一个leader,此时首先会比较每一台机器的czxid,czxid最大的被选为leader。极端情况,czxid都相等的情况,那么就会直接比较myid。

监听器原理

  1. 首先有一个main()线程

  2. 在main线程中创建ZK客户端,这是创建两个线程:一个负责网络连接通信connect,一个负责监听listener

  3. 通过connnect线程将注册的监听事件发送给ZK

  4. 在ZK的注册监听器列表中将注册的监听事件添加至列表

  5. ZK监听到有数据或者路径变化时,将消息发送给listener

  6. listener内部调用process方法

部署方式

部署方式分为单机模式,集群模式和伪集群模式

  • 单机部署:一般用来检验 Zookeeper 基础功能,熟悉 Zookeeper 各种基础操作及特性。
  • 伪集群部署:在单台机器上部署集群,方便在本地验证集群模式下的各种功能。
  • 集群部署:一般在生产环境使用,具备一致性、分区容错性。

ZK常用命令

zkServer.sh start

zkServer.sh status

zkCli.sh 启动客户端

ls /

get /

delete

API调用

public class ZookeeperTest {
    /**
     * 1. 获取ZK客户端连接对象
     * 2. 调用相关API完成对应功能
     * 3. 关闭资源
     *
     */
    private ZooKeeper zk;
    @Before
    public void init() throws IOException {
        // String connectString, int sessionTimeout, Watcher watcher
        // connectString 连接zk服务的地址 hadoop102:2181, hadoop103:2181, hadoop104:2181
        // sessionTimeout 超时时间
        String connectStr="hadoop102:2181, hadoop103:2181, hadoop104:2181";
        int sessionTimeout=10000;
        zk= new ZooKeeper(connectStr, sessionTimeout, new Watcher() {
            public void process(WatchedEvent watchedEvent) {
                System.out.println("根据具体业务进行下一步操作(监听事件发生了,该做什么)");
            }
        });
    }
    @After
    public void close() throws InterruptedException {
        zk.close();
    }

    @Test
    /**
     * 创建节点
     * 1.path:指定创建节点的路径
     * 2.data:指定要创建的节点下的数据
     * 3.acl:对操作用户的权限控制
     * 4.createMode:指定当前节点的类型(持久化、临时、临时+序列、持久化+序列
     */
    public void testCreate() throws KeeperException, InterruptedException {
        //    final String path, byte data[], List acl,CreateMode createMode
        zk.create("/honglou","jiabaoyu".getBytes(),
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    // 获取子节点
    @Test
    public void testGetChild() throws KeeperException, InterruptedException {
        //String path, boolean watch
//        不监控:
        // List children = zk.getChildren("/", false);
//        监控
        List<String> children = zk.getChildren("/", new Watcher() {
            public void process(WatchedEvent event) {
                System.out.println("你好。" );
            }
        });
        for (String child : children) {
            System.out.println(child);
        }
    }

    // 判断节点是否存在
    @Test
    public void testExist() throws KeeperException, InterruptedException {
        Stat exists = zk.exists("/", false);
        if (exists==null)
            System.out.println("节点不存在");
        else
            System.out.println("节点存在");
    }

    /**
     * 获取子节点数据
     */
    @Test
    public void testGetData() throws KeeperException, InterruptedException {
        //final String path, Watcher watcher, Stat stat
        // 判断节点是否存在
        String path="/sanguo";
        Stat stat=zk.exists(path,false);
        System.out.println("======================");
        if (stat==null)
            System.out.println("节点不存在");
        else {
            byte[] data = zk.getData(path, false, stat);
            System.out.println(new String(data));
        }
    }

    /**
     * 修改节点
     */
    @Test
    public void testSetData() throws KeeperException, InterruptedException {
        String path="/sanguo";
        Stat stat=zk.exists(path,false);
        if (stat==null)
            System.out.println("节点不存在");
        else {
            //final String path, byte data[], int version
            // version 版本号,保证数据的一致性
            zk.setData(path,"zhuge,abei".getBytes(),stat.getVersion());
        }
    }

    /**
     * 删除单个节点
     *
     */
    @Test
    public void testDel() throws KeeperException, InterruptedException {
        String path="/sanguo/shuhan";
        Stat stat=zk.exists(path,false);
        if (stat==null)
            System.out.println("节点不存在");
        else {
            //final String path, byte data[], int version
            // version 版本号,保证数据的一致性
            zk.delete(path,stat.getVersion());
        }
    }

    /**
     * 删除有子节点的节点
     * 使用递归
     */
    @Test
    public void testDelAll() throws KeeperException, InterruptedException {
        delAll("/a",zk);
    }

    public void delAll(String path,ZooKeeper zk) throws KeeperException, InterruptedException {

        Stat stat=zk.exists(path,false);
        if (stat==null)
            System.out.println("节点不存在");
        else {
            //获取当前节点的子节点
            List<String> children = zk.getChildren(path, false);
            if (children.isEmpty()){
                zk.delete(path,stat.getVersion());
            }
            else {
                // 递归调用自己,先把所有子节点删除
                for (String child : children) {
                    delAll(path+"/"+child,zk);
                }
                //最后删除自己
                delAll(path,zk);
            }

        }
    }
}

你可能感兴趣的:(大数据,zookeeper,分布式,大数据)