大数据 ZooKeeper

前言

ZooKeeper是一个为分布式应用所设计的开源协调服务。

介绍

ZooKeeper是一个为分布式应用所设计的开源协调服务。它主要为用户提供同步、配置管理、分组和命名等服务,减轻分布式应用程序所承担的协调任务。ZooKeeper的文件系统使用了我们所熟悉的目录树结构。ZooKeeper是使用Java编写的,但是它支持JavaC两种编程语言。

设计目标

目标说明
简单化ZooKeeper允许分布式的进程通过共享体系的命名空间来进行协调,ZooKeeper采用Znode搭建与标准文件系统类似的命名空间【可以参见下一章“ ZooKeeper 命名空间”】,且该命名空间是存放在内存中的,这就意味着ZooKeeper具备高吞吐量、低延迟的能力。ZooKeeper具备高性能、高可靠性以及严格的有序访问。
健壮性使用心跳来检测服务器的状态。如果有服务器失联,就连接到其他备用服务器上。
有序性ZooKeeper可以为每一次更新操作赋予一个版本号,并且此版本号是全局有序的,不存在重复的情况。
速度优势读性能优于写性能。
原子性在命名空间中,每一个Znode的数据将被原子地读写。读操作将读取与Znode相关的所有数据,写操作将替换掉所有的数据。每一个结点都有一个访问控制表,规定了用户操作的权限。
可靠性
时效性

ZooKeeper 命名空间

ZooKeeper拥有一个层次的命名空间。在命名空间中,每个结点称为Znode,每个Znode包含了它自身和它的子节点相关联的数据。指向结点的路径必须使用规范的绝对路径表示,并且以“/”来分隔。【在ZooKeeper中不允许使用相对路径来表示】

在这里插入图片描述

每个Znode维护着一个属性结构,包含了数据的版本号dataVersion,时间戳ctime、mtime等状态信息。

[zk: localhost:2181(CONNECTED) 14] get /app2
aaabbb
cZxid = 0x600000004 # 创建事务编号
ctime = Thu Sep 13 21:47:07 EDT 2018 
mZxid = 0x600000004 # 修改事务编号
mtime = Thu Sep 13 21:47:07 EDT 2018
pZxid = 0x600000004 # 持久化事务编号
cversion = 0 # 创建版本
dataVersion = 0
aclVersion = 0 # 权限版本
ephemeralOwner = 0x65d5b998e80000
dataLength = 6 # 数据长度
numChildren = 0 # 子节点个数

看不懂这些属性,可以参见【Znode 属性

Znode 主要特征

特征说明
Watch客户端可以在节点上设置Watch,当节点的数据发生变化(增删改等操作)将会出发Watch的对应的操作。【Watch有且仅会被触发一次并发送一个通知。】
数据访问ZooKeeper中的每个节点上存储的数据需要被原子性的操作。
临时节点ZooKeeper中的节点有两种,分别为临时节点和永久节点。节点的类型在创建后不能改变。ZooKeeper临时节点的生命周期依赖于创建它们的Session。一旦Session结束,临时节点将被自动删除,当然也可以手动删除。ZooKeeper的临时节点不允许拥有子节点。相反,永久节点的生命周期不依赖于Session,并且只有在客户端显示执行删除操作的时候,它们才被删除。
顺序结点【唯一性】当创建Znode的时候,用户可以请求在ZooKeeper的路径结尾添加一个递增的计数。这个计数对于此节点的父节点来说是唯一的,它的格式为“%010d”(10位数字,没有数值的数据位用0填充,例如0000000001)。当计数值大于232-1时,计数器将会溢出。

Znode 属性

属性说明
czxid节点被创建的Zxid值。
mzxid节点被修改的Zxid值。
ctime节点被创建的时间。
mtime节点被修改的时间。
version节点被修改的版本号。
cversion节点所拥有的子节点被修改的版本号。
aversion节点的ACL被修改的版本号。
ephemeralOwner如果此节点是临时节点,那么它的值就是这个节点拥有者的Session ID。否则,它的值为0。
numChildren节点拥有的子节点的个数。

ZooKeeper ACL

ZooKeeper使用ACL对Znode进行访问控制。ACL拥有三种模式:

  • user:文件的拥有者
  • group:用户组
  • world:

一个ACL和一个ZooKeeper节点是对应的,并且不存在继承关系,相互独立。

访问控制权限所规定的权限

权限权限描述
CREATE创建子节点。
READ从节点获取数据或者列出节点的所有子节点。
WRITE设置节点的数据。
DELETE删除子节点。
ADMIN可以设置权限。

验证模式

模式说明
world代表某一特定的用户(Client)。
auth代表任何已经通过验证的用户(Client)。
digest通过用户密码进行验证。
ip通过Client IP地址进行验证。

ZooKeeper一致性保证

一致性特点说明
顺序一致性Client的更新顺序与它们发送的顺序相一致的。
原子性更新操作只有失败和成功两种结果。
单系统镜像ZooKeeper视图在不同服务器上都一致。
可靠性一旦一个更新操作被应用,那么在更新之前,其值都不会改变。
实时性在特定的一段时间内,客户端看到的系统需要被保证是实时的(在十几秒的时间里)。在此时间段内,任何系统的改变将被客户端看到,或者被客户端侦测到。

ZooKeeper Leader选举

搭建环境

  • 环境:VMWare下的四个Centos7虚拟机。

首先,当然是先下载环境啦~点击跳转到下载页面

  • 修改主机名:(也可以不修改)
    • vim /etc/sysconfig/network
    • 增加:HOSTNAME=hadoop
  • 下载:wget http://mirrors.hust.edu.cn/apache/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz
  • 解压:tar -zxvf zookeeper-3.4.10.tar.gz
  • 移动到/opt目录下:
    • sudo mv zookeeper-3.4.10 /opt
    • cd /opt
    • sudo mv zookeeper-3.4.10 zookeeper
  • 修改配置文件:
    • cd conf
    • cp zoo_sample.cfg zoo.cfg
    • vim zoo.cfg
    • 修改dataDir目录:
      • mkdir /opt/zookeeper/zooData
      • 修改dataDir=/tmp/zookeeper/opt/zookeeper/zooData
    • 增加Log输出目录:
      • mkdir /opt/zookeeper/zooLog
      • dataLogDir=/opt/zookeeper/zooLog
    • 增加服务端:
      • 增加一个IP地址别名,后面配置使用。
        • vim /ect/hosts
        • 说明:IP地址 别名
        • 192.168.80.8 node0
        • 192.168.80.9 node1
        • 192.168.80.10 node2
        • 192.168.80.11 node3
      • 说明:等号后参数分别为:主机名、心跳端口、数据端口
        • server.1=node1:2888:3888
        • server.2=node2:2888:3888
        • server.3=node3:2888:3888
    • 配置myid
      • 在每个服务端都需要这样子配,但是myid不允许相同
      • myid就是上述的server.数字的数字,即server.1中的1就是myid的值。
      • echo 0 > /opt/zookeeper/zooData/myid

免密登录访问

  • 配置SSH免密登录访问。
    • 生成公钥秘钥:ssh-keygen,一路回车即可。
    • 进入~/.ssh文件夹,创建一个touch authorized_keys文件,将需要SSH免登录的服务端的公钥id_rsa.pub中的内容复制到authorized_keys中即可。

分发Zookeeper

  • 将配置好的zookeeper文件夹分发到其他的服务端上,记得修改myid,和按照上修改配置文件部分重新修改一遍。
    • scp -r /opt/zookeeper root@node1:/opt/

关闭防火墙

  • 关闭防火墙(其实也可以单独开某几个端口吧,为了方便,自行选择哈~
    • Centos7
      • systemctl stop firewalld && systemctl disable firewalld
    • Centos6
      • service iptables stop

启动zookeeper

全部配置完毕之后,就是启动zookeeper了。
如果启动不成功的话,可以看看zookeeper/bin/zookeeper.out下的日志输出。

  • 启动
    • ./bin/zkServer.sh start
  • 查看状态
    • ./bin/zkServer.sh status
  • 启动输出如下:
[root@node3 bin]# ./zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
  • 启动成功如下:
    • 下面leaderMaster服务器,这个是选举出来的,如果Master挂掉,会重新随机选举出来。
    • followerSlave服务器。
[root@node3 bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: leader 

[root@node2 bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: follower

ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: follower
  • 如果kill -9leader,那么其他节点就会重新投票产生leader
  • 半数机制:集群中半数以上机器存活,集群可用。如果低于半数的机器存活,那么就会出现Error contacting service. It is probably not running.

zkCli Command

使用./bin/zkCli.sh启动命令行,使用help可以查看帮助文档。

  • ls path [watch]
    • ls 路径 监听
[zk: localhost:2181(CONNECTED) 6] ls /
[zookeeper]
  • create [-s] [-e] path data acl
    • create 序号 结点类型 路径 数据 权限
    • 结点类型有两种:
      • 短暂ephemeral:断开连接就会删除自身结点。
      • 永久persistent:断开连接不删除自身结点。
      • Znode有四种形式的结点(默认是persistent
        • PERSISTENT
        • PERSISTENT_SEQUENTIAL(持久序列/test0000000019 )
        • EPHEMERAL
        • EPHEMERAL_SEQUENTIAL
[zk: localhost:2181(CONNECTED) 6] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 7] create /app1 aaa
Created /app1
[zk: localhost:2181(CONNECTED) 8] ls /
[zookeeper, app1]
[zk: localhost:2181(CONNECTED) 9] create -s /app2 aaa
Created /app20000000001
[zk: localhost:2181(CONNECTED) 10] ls /
[app20000000001, zookeeper, app1]
[zk: localhost:2181(CONNECTED) 11] create -e /app2 aaabbb
Created /app2
[zk: localhost:2181(CONNECTED) 12] ls /                  
[app20000000001, zookeeper, app2, app1]
  • get path [watch],获取数据
    • get 路径 监听
[zk: localhost:2181(CONNECTED) 14] get /app2
aaabbb
cZxid = 0x600000004 # 创建事务编号
ctime = Thu Sep 13 21:47:07 EDT 2018 
mZxid = 0x600000004 # 修改事务编号
mtime = Thu Sep 13 21:47:07 EDT 2018
pZxid = 0x600000004 # 持久化事务编号
cversion = 0 # 创建版本
dataVersion = 0
aclVersion = 0 # 权限版本
ephemeralOwner = 0x65d5b998e80000
dataLength = 6 # 数据长度
numChildren = 0 # 子节点个数
  • set path data [version],设置结点数据
    • set 路径 数据 [版本号]
# 设置数据为Hello_world
[zk: localhost:2181(CONNECTED) 15] set /app2 Hello_world 
cZxid = 0x600000004
ctime = Thu Sep 13 21:47:07 EDT 2018
mZxid = 0x600000005
mtime = Thu Sep 13 22:17:11 EDT 2018
pZxid = 0x600000004
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x65d5b998e80000
dataLength = 11
numChildren = 0
[zk: localhost:2181(CONNECTED) 16] get /app2
Hello_world # 重新设置的数据
cZxid = 0x600000004
ctime = Thu Sep 13 21:47:07 EDT 2018
mZxid = 0x600000005
mtime = Thu Sep 13 22:17:11 EDT 2018
pZxid = 0x600000004
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x65d5b998e80000
dataLength = 11
numChildren = 0
  • delete path [version]
    • delete 路径 版本号
[zk: localhost:2181(CONNECTED) 18] delete /app2
[zk: localhost:2181(CONNECTED) 19] ls /app2
Node does not exist: /app2

监听数据的变化

  • get path [watch]
    • get 路径 监听器
    • 监听效果有且仅有一次,换句话时候,就是只能监听一次,一次之后就失效了,需要重新建立监听器。

Java API使用

依赖库

  • log4j-1.2.17.jar
  • zookeeper-3.4.6.jar

代码示例

import org.apache.log4j.BasicConfigurator;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.List;

public class Test1 {
    private static ZooKeeper zk = null;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        // 配置Log4J
        BasicConfigurator.configure();
        // 建立连接
        int sessionTimeout = 2000;
        // 注意:node1、node2、node3需要在/etc/hosts中配置以下内容
        // 192.168.80.9 node1
        // 192.168.80.10 node2
        // 192.168.80.11 node3
        String connectString = "node1:2181,node2:2181,node3:2181";
        zk = new ZooKeeper(connectString, sessionTimeout, watchedEvent -> System.out.println("时间触发……"));
    }

    /**
     * create方法参数
     * String path, byte[] data, List<ACL> acl, CreateMode createMode
     * path:路径
     * data:值bytes
     * acl:对节点的访问控制
     * createMode:节点的类型——短暂,永久,序号
     */
    @Test
    public void create() throws KeeperException, InterruptedException {
        String path = zk.create("/app3", "hello_world".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println(path);
    }

    /**
     * 判断节点是否存在
     */
    @Test
    public void exist() throws KeeperException, InterruptedException {
        String nodeName = "/app1";
        // 设置为true会调用zk中的监听器
        Stat stat = zk.exists(nodeName, true);
        if (stat != null) {
            System.out.println("节点“" + nodeName + "”存在,长度为:" + stat.getDataLength());
        } else {
            System.out.println("节点" + nodeName + "不存在的。");
        }
    }

    /**
     * 获取子节点
     */
    @Test
    public void getChildren() throws KeeperException, InterruptedException {
        List<String> list = zk.getChildren("/", true);
        for (String str : list) {
            System.out.println(str);
        }
    }

    /**
     * 获取子节点
     * getChildren(path, watch) 监听的事件是:节点下的子节点增减变化事件。
     */
    @Test
    public void getChildren1() throws KeeperException, InterruptedException {
        List<String> list = zk.getChildren("/", watchedEvent -> System.out.println("监控节点下的子节点被改变->" + watchedEvent.getPath()));
        for (String str : list) {
            System.out.println(str);
        }
        Thread.sleep(Long.MAX_VALUE);
    }

    /**
     * 获取节点的内容
     * getData(path,watch) 监听的事件是:节点数据变化事件。
     */
    @Test
    public void getData1() throws KeeperException, InterruptedException {
        byte[] bytes = zk.getData("/app1", watchedEvent -> System.out.println("数据改变了……"), null);
        System.out.println(new String(bytes));
    }

    /**
     * 获取节点的内容
     */
    @Test
    public void getData() throws KeeperException, InterruptedException {
        byte[] bytes = zk.getData("/app1", false, null);
        System.out.println(new String(bytes));
    }

    /**
     * 修改节点的内容
     * version为-1时表示自动维护
     */
    @Test
    public void setData() throws KeeperException, InterruptedException {
        Stat stat = zk.setData("/app1", "test".getBytes(), -1);
        System.out.println(stat.toString());
    }

    /**
     * 删除节点,非空节点删除不掉。
     * version为-1时表示自动维护
     */
    @Test
    public void delete() throws KeeperException, InterruptedException {
        zk.delete("/app1", -1);
    }
}

附录

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值