Zookeeper知识点总结

基础

  • Zookeeper = 文件系统 + 通知机制

  • Apach Hbase和 Apache solr 以及 Dubbo等项目都采用了Zookeeper

  • Zookeeper是一个分布式的、高性能的,开源的分布式系统的协调服务,是Google的Chubby一个开源的实现,是Hadoop 和 Hbase 的重要组件,是一个为分布式应用提供数据一致性服务的软件。具有严格顺序访问控制能力的分布式协调存储服务

  • 是一个基于观察者模式设计的分布式服务管理框架,负责存储和管理大家都关心的数据,然后接收观察者Watch的注册,一旦这些数据的状态发生变化,Zookeeper负责通知已经在Zookeeper上注册的那些观察者做出相应的反应,从而实现集群中类似Master/Slave管理模式

  • Zookeeper应用场景:

    • 维护配置信息:作为配置中心,保证配置项配置信息一致性,从而达到维护配置信息的效果
    • 分布式锁服务:多台服务器运行同一种服务,协调个服务的进度。为了保证当某个服务需要进行同步操作时,Zookeeper可以对该操作加锁
    • 集群管理:集群管理各种服务器,当一些服务器加入/移出时会通知给其他正常工作的服务器,以即使调整和分配任务。还对故障服务器诊断并尝试修复
    • 生成分布式唯一ID:分库分表后无法再以靠数据库的auto_increment自动生成唯一ID,此时Zookeeper可以在生成新id时创建持久顺序节点,创建操作返回的节点符号,即为新Id,然后把比自己节点小的删除即可。
  • Zookeeper提供了一套分布式集群管理机制,基于层次性的目录树的数据结构(文件系统),并对树中的结点进行有效管理,从而设计出各种分布式数据管理模型,作为分布式系统的沟通调度桥梁。

  • Zookeeper Service看作是班主任,client看作学生,watch看作是微信,config Data看作是通知信息,一处更新处处更新

image-20200713152246279

能干吗:
  • 命名服务
  • 配置维护
  • 集群管理、
  • 分布式消息同步和协调机制
  • 负载均衡(大多还是用nginx)
  • 对Dubbo的支持
怎么玩
  • 统一命名服务(Name Service 如 Dubbo服务注册中心)
  • 配置管理(Configuration Management,如淘宝考员配置管理框架Diamond)
  • 集群管理(Group Membership, 如Hadoop分布式集群管理)
zookeeper配置文件zoo.cfg
  • ls -ltr :该linux命令工作中很常用,作用是按创建顺序显示当前目录下的文件和目录

  • 5大参数:

    • tickTime=2000:通信心跳数,Zookeeper服务器心跳时间,单位为毫秒。服务器与客户端或服务器之间维持心跳的时间间隔
    • initLimit=10:集群中follower跟随着服务器(F) 与leader领导者服务器(L)之间初始连接时能容忍的最多心跳书(tickTime的数量)。即主机和从机连接所需的时间
    • syncLimit=5:LF同步通信时限,Leader和Follower之间的最大响应时间单位,加入超过 syncLimit * tickTime,Leader认为Follower死掉,从服务器列表删除Follower
    • dataDir=/tmp/zookeeper:数据存放的目录,默认在临时目录temp,一般需要修改到指定目录。
    • clientPort=2182 :默认端口为2182
使用
  • 进入bin:./zkServer.sh start,启动zookeeper服务
  • ./zkCli.sh开启客户端
  • zookeeper服务器端的四字命令(在linux下使用而不是zookeeper下):
    • echo ruok | nc 127.0.0.1 2181 :查看zookeeper的服务器端是否准备就绪
    • echo stat | nc 127.0.0.1 2181 :查看zookeeper服务器端状态
    • echo envi | nc 127.0.0.1 2181 :查看zookeeper服务器端环境
  • create /testNode v1:创建节点 get /testNode :获取节点的data。
  • set /testNode v2:覆盖 testNode的value
  • get /testNode :查询该节点的数据
  • ls/ls2 /testNode :查询该节点信息,ls只显示子节点,ls2显示所有信息
  • 删除节点:delete/rmr /testNode
zookeeper数据模型
  • Zookeeper所使用的数据模型风格很像文件系统的目录树结构。有点类似windows中注册表的结构
  • 有名称、有树节点,有键值对的关系
  • 可以看作是一个树形结构的数据库,但又不能存放数据,作用是对分布在不同机器上做名称管理
  • Stat结构体:
    • image-20200713173217271
数据模型znode节点
  • Zookeeper数据模型结构与Unix文件系统很类似,整体看作一棵树,自身维护一套存储数据结构,每个节点是一个ZNode

  • 每一个znode默认能存储1MB数据,每个ZNode都可以通过其路径唯一标识

  • create /node1/node2 val :报错,不支持多级创建节点

  • Znode = path + nodeValue + Stat 。即 set /path nodeValue,自带Stat,存放该节点的一些信息

  • znode中的存在类型:持久,临时。但细分要有四种

  • create -s /myNode v2 : -s 表示节点自动从myNode后面添加序列号,确保该节点不重复

  • create -e /myNode v2 : -e 代表临时节点,重启将失效,默认-p(可不写)表示持久化。-s和-e可同时用。

  • 一个节点对应一个应用,节点存储的数据就是应用所需要的配置信息

通话机制:Session + Watch
  • 客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变,被删除、子目录节点增加删除)时,zookeeper会通知客户端。

  • Session :通过Java建立Zookeeper会话,此时处于CONNECTING状态,当客户端连接Zookeeper服务成功后进入CONNECTED状态 ,结束状态CLOSED

  • Watch(观察者)

    • 异步+回调+的触发机制
    • 异步和回调的理解:假如面试我一结束,面试官口渴托我买瓶可乐,如果在我去买可乐过程中面试官一直在等而不继续面试,这叫同步;若在我买水的过程中面试继续进行,这是异步;若买水时发现没有可乐,这时打电话询问面试官说明情况的这个过程,就叫异步回调。
    • 客户端 可以在每个znode节点上设置一个Watcher,如果被观察的服务端的znode节点有变更,watch会触发,这和watch所属的客户端将接收到一个通知包被告知节点已经变化,把响应的事件通知给设置Watcher的Client端
    • zookeeper里所有读取操作:getData() , getCildren() 和 exists() 都有设置watch的选项
  • watch事件理解

    • 1.一次性触发:只监控一次,触发一次后不再有效。一般不多用
    • 2.发送客户端
    • 3.为数据设置watch
    • 4.时序性和一致性

Java操作Zookeeper

Maven工程和配置POM
  • 依赖

            <!-- 注册中心使用的是zookeeper,引入操作zookeeper的客户端 -->
    		<dependency>
    			<groupId>org.apache.curator</groupId>
    			<artifactId>curator-framework</artifactId>
    			<version>2.12.0</version>
    		</dependency>
            <dependency>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
                <version>RELEASE</version>
            </dependency>
    
  • WatchOne 一次性触发watch,数据监控

public class WatchOne {
    private static final Logger logger = LoggerFactory.getLogger(WatchOne.class);
    private final static String CONNECTSTRING = "192.168.137.133:2181";
    private final static int SESSION_TIMEOUT = 50 * 1000;
    private final static String PATH = "/atguigu";
    private ZooKeeper zk = null;

    public ZooKeeper startZK() throws Exception {
        return new ZooKeeper(CONNECTSTRING, SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
            }
        });
    }

    public void stopZK() throws Exception {
        if(zk != null)  zk.close();
    }

    public void createZnode(String nodePath, String nodeValue) throws Exception {
        // OPEN_ACL_UNSAFE:类似关闭防火墙
        // CreateMode.PERSISTENT: 创建的节点由Java控制是持久的
        zk.create(nodePath, nodeValue.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    public String getZnode(String nodePath) throws Exception {
        String result = null;
        byte[] byteArray = zk.getData(PATH, new Watcher() { // 一次性触发
            @Override
            public void process(WatchedEvent event) {
                try {
                    trigerValue(PATH);
                }  catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, new Stat());
        result = new String(byteArray); // 转为字符串
        return result;
    }

    public String trigerValue(String nodePath) throws Exception {
        String result = null;
        byte[] byteArray = zk.getData(PATH, false, new Stat());
        result = new String(byteArray); // 转为字符串
        return result;
    }

    public static void main(String[] args) throws Exception {
        WatchOne watchOne = new WatchOne();
        watchOne.setZk(watchOne.startZK());
        if(watchOne.getZk().exists(PATH, false) == null){ // 没有该节点才能创建,不然会报错,节点是不能覆盖的
            watchOne.createZnode( PATH, "AAA");
            String retValue = watchOne.getZnode(PATH);
            logger.info("retValue="+retValue);
            Thread.sleep(Long.MAX_VALUE);
        }else{
            logger.info("I have no znode");
        }
    }
    public ZooKeeper getZk() {
        return zk;
    }
    public void setZk(ZooKeeper zk) {
        this.zk = zk;
    }
}
WatchMore 长久数据监控(常用)
public class WatchMore {
    private static final Logger logger = LoggerFactory.getLogger(WatchMore.class);
    private final static String CONNECTSTRING = "192.168.137.133:2181";
    private final static int SESSION_TIMEOUT = 50 * 1000;
    private final static String PATH = "/atguigu";
    private ZooKeeper zk = null;
    private String oldValue = null;

    public ZooKeeper startZK() throws Exception {
        return new ZooKeeper(CONNECTSTRING, SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
            }
        });
    }

    public void stopZK() throws Exception {
        if(zk != null)  zk.close();
    }

    public void createZnode(String nodePath, String nodeValue) throws Exception {
        // OPEN_ACL_UNSAFE:类似关闭防火墙
        // CreateMode.PERSISTENT: 创建的节点由Java控制是持久的
        zk.create(nodePath, nodeValue.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    public String getZnode(String nodePath) throws Exception {
        String result = null;
        byte[] byteArray = zk.getData(PATH, new Watcher() { // 一次性触发
            @Override
            public void process(WatchedEvent event) {
                try {
                    trigerValue(PATH);
                }  catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, new Stat());
        result = new String(byteArray); // 转为字符串
        oldValue = result; // 一开始初始值
        return result;
    }

	// 每次所观察的节点改变就触发 
    public boolean trigerValue(String nodePath) throws Exception {
        String result = null;
        byte[] byteArray = zk.getData(PATH, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                try {
                    trigerValue(nodePath);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, new Stat());
        result = new String(byteArray); // 转为字符串
        String newValue = result;
        if(oldValue.equals(newValue)){
            logger.info("*********no changes");
            return false;
        }else{
            logger.info("************oldValue:"+oldValue+"\t newValue:"+newValue);
            oldValue = newValue;
            return true;
        }
    }
	// 监控节点 /atguigu下的数据
    public static void main(String[] args) throws Exception {
        WatchMore watchOne = new WatchMore();
        watchOne.setZk(watchOne.startZK());
        if(watchOne.getZk().exists(PATH, false) == null){ // 没有该节点才能创建,不然会报错,节点是不能覆盖的
            watchOne.createZnode( PATH, "AAA");
            String retValue = watchOne.getZnode(PATH);
            logger.info("retValue="+retValue);
            Thread.sleep(Long.MAX_VALUE);
        }else{
            logger.info("I have no znode");
        }
    }
    public ZooKeeper getZk() {
        return zk;
    }
    public void setZk(ZooKeeper zk) {
        this.zk = zk;
    }

    public String getOldValue() {
        return oldValue;
    }

    public void setOldValue(String oldValue) {
        this.oldValue = oldValue;
    }
}
子节点变化监控(不常用)
package com.xuecheng.manage_course;

import org.apache.zookeeper.*;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;


public class WatchChild {
    private static final Logger logger = LoggerFactory.getLogger(WatchChild.class);

    private final static String CONNECTSTRING = "192.168.137.133:2181";
    private final static int SESSION_TIMEOUT = 50 * 1000;
    private final static String PATH = "/atguigu";
    private ZooKeeper zk = null;

    public ZooKeeper startZK() throws Exception {
        return new ZooKeeper(CONNECTSTRING, SESSION_TIMEOUT, new Watcher() { // 开启监控
            @Override
            public void process(WatchedEvent event) {
                // 子节点没改变
                if(event.getType() == EventType.NodeChildrenChanged && event.getPath().equals(PATH)){
                    showChildNode(PATH); // 子节点每次改变都会执行该方法
                }else{
                    showChildNode(PATH); // 一开始会注册父亲节点并打印初始子节点
                }
            }
        });
    }

    public void showChildNode(String nodePath) {
        List<String> list = null;
        try {
            // 获取该节点所有子节点
            list = zk.getChildren(PATH, true); // true表示该节点下全部监控,自带连续监控
            logger.info("***********"+list);
        } catch (KeeperException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 监控子节点
    public static void main(String[] args) throws Exception {
        WatchChild watchChild = new WatchChild();
        watchChild.setZk(watchChild.startZK());
        Thread.sleep(Long.MAX_VALUE);

    }
    public ZooKeeper getZk() {
        return zk;
    }
    public void setZk(ZooKeeper zk) {
        this.zk = zk;
    }
}

Zookeeper集群
  • 分为服务器端和客户端,客户端之连接到某个服务器上,客户端使用并维护一个TCP连接,第一次连接会建立会话,当这个客户端连接到另外服务器时,这个会话会被新的服务器重新建立

  • 配置项书写格式:server.N = YYY:A:B,其中,N为服务器编号,YYY表示服务器IP地址,A为LF(主从机)通信端口,即该服务器与集群中的leader交换的信息的端口。B为选举端口,即选举新leader时服务器间相互通信的端口(当leader挂掉后,其余服务器会相互通信并选举出新的leader)

  • 真集群中每个服务器A端口和B端口都是一样的。

  • 伪集群中每个服务器A端口和B端口都是不一样的。但IP一样

  • 伪集群做法:

    • 分别复制zookeeper目录为zk01、zk02、zk03并分别在配置文件修改以下:

image-20200714010132087

image-20200714010140415

​ 此时使用zk01和zk02作为服务器,zk03连接server作为客户端:./zkCli.sh -server 127.0.0.1:2193

​ 此时使用zk03改变节点数据,zk01和zk02便可查到刚改变的数据,完成伪集群

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值