2. zookeeper介绍及集群搭建

 

ZooKeeper 概述

Zookeeper 是一个分布式协调服务的开源框架。 主要用来解决分布式集群中
应用系统的一致性问题,例如怎样避免同时操做同一数据形成脏读的问题。
ZooKeeper 本质上是一个分布式的小文件存储系统。 提供基于相似于文件系
统的目录树方式的数据存储,而且能够对树中的节点进行有效管理。从而用来维
护和监控你存储的数据的状态变化。经过监控这些数据状态的变化,从而能够达
到基于数据的集群管理。 诸如: 统一命名服务(dubbo)、分布式配置管理(solr的配置集中管理)、分布式消息队列(sub/pub)、分布式锁、分布式协调等功能。java

2.一、zookeeper的架构图

Leader:
Zookeeper 集群工做的核心
事务请求(写操做) 的惟一调度和处理者,保证集群事务处理的顺序性;
集群内部各个服务器的调度者。
对于 create, setData, delete 等有写操做的请求,则须要统一转发给leader 处理, leader 须要决定编号、执行操做,这个过程称为一个事务。
Follower:
处理客户端非事务(读操做) 请求,node

转发事务请求给 Leader;linux

参与集群 Leader 选举投票 2n-1台能够作集群投票。算法

此外,针对访问量比较大的 zookeeper 集群, 还可新增观察者角色。shell

 


Observer:
观察者角色,观察 Zookeeper 集群的最新状态变化并将这些状态同步过
来,其对于非事务请求能够进行独立处理,对于事务请求,则会转发给 Leader
服务器进行处理。
不会参与任何形式的投票只提供非事务服务,一般用于在不影响集群事务
处理能力的前提下提高集群的非事务处理能力。数据库

扯淡:说白了就是增长并发的读请求apache

2.二、zookeeper的特性

1.全局数据一致:每一个 server 保存一份相同的数据副本, client 不管连
接到哪一个 server,展现的数据都是一致的,这是最重要的特征;
2. 可靠性:若是消息被其中一台服务器接受,那么将被全部的服务器接受。
3. 顺序性:包括全局有序和偏序两种:全局有序是指若是在一台服务器上
消息 a 在消息 b 前发布,则在全部 Server 上消息 a 都将在消息 b 前被
发布;偏序是指若是一个消息 b 在消息 a 后被同一个发送者发布, a 必
将排在 b 前面。
4. 数据更新原子性:一次数据更新要么成功(半数以上节点成功),要么失
败,不存在中间状态;
5. 实时性: Zookeeper 保证客户端将在一个时间间隔范围内得到服务器的
更新信息,或者服务器失效的信息。vim

2.三、三台机器zookeeper的集群环境搭建

Zookeeper 集群搭建指的是 ZooKeeper 分布式模式安装。 一般由 2n+1
台 servers 组成。 这是由于为了保证 Leader 选举(基于 Paxos 算法的实
现) 能过获得多数的支持,因此 ZooKeeper 集群的数量通常为奇数。
Zookeeper 运行须要 java 环境, 因此须要提早安装 jdk。 对于安装
leader+follower 模式的集群, 大体过程以下:
l 配置主机名称到 IP 地址映射配置
l 修改 ZooKeeper 配置文件
l 远程复制分发安装文件
l 设置 myid
l 启动 ZooKeeper 集群
若是要想使用 Observer 模式,可在对应节点的配置文件添加以下配置:
peerType=observer
其次,必须在配置文件指定哪些节点被指定为 Observer,如:
server.1:localhost:2181:3181:observer服务器

服务器IPsession

主机名

myid的值

192.168.221.100

node01

1

192.168.221.110

node02

2

192.168.221.120

node03

3

 

第一步:下载zookeeeper的压缩包,下载网址以下

http://archive.apache.org/dist/zookeeper/

咱们在这个网址下载咱们使用的zk版本为3.4.9

下载完成以后,上传到咱们的linux的/export/softwares路径下准备进行安装

第二步:解压

解压zookeeper的压缩包到/export/servers路径下去,而后准备进行安装

cd /export/softwares

tar -zxvf zookeeper-3.4.9.tar.gz -C ../servers/

第三步:修改配置文件

第一台机器修改配置文件

cd /export/servers/zookeeper-3.4.9/conf/

cp zoo_sample.cfg zoo.cfg

mkdir -p /export/servers/zookeeper-3.4.9/zkdatas/

vim  zoo.cfg

dataDir=/export/servers/zookeeper-3.4.9/zkdatas

autopurge.snapRetainCount=3

autopurge.purgeInterval=1

server.1=node01:2888:3888

server.2=node02:2888:3888

server.3=node03:2888:3888

 

第四步:添加myid配置

在第一台机器的

/export/servers/zookeeper-3.4.9/zkdatas/这个路径下建立一个文件,文件名为myid ,文件内容为1

echo 1 > /export/servers/zookeeper-3.4.9/zkdatas/myid

第五步:安装包分发并修改myid的值

安装包分发到其余机器

第一台机器上面执行如下两个命令

scp -r  /export/servers/zookeeper-3.4.9/ node02:/export/servers/

scp -r  /export/servers/zookeeper-3.4.9/ node03:/export/servers/

第二台机器上修改myid的值为2

echo 2 > /export/servers/zookeeper-3.4.9/zkdatas/myid

三台机器上修改myid的值为3

echo 3 > /export/servers/zookeeper-3.4.9/zkdatas/myid

第六步:三台机器启动zookeeper服务

三台机器启动zookeeper服务

这个命令三台机器都要执行

/export/servers/zookeeper-3.4.9/bin/zkServer.sh start

查看启动状态

/export/servers/zookeeper-3.4.9/bin/zkServer.sh  status

 

2.四、zookeeper的shell操做

2.4.一、客户端链接

运行 zkCli.sh –server ip 进入命令行工具。

输入 help,输出 zk shell 提示:

2.4.二、shell操做

建立节点

 

create [-s] [-e] path data acl

 

其中,-s 或-e 分别指定节点特性,顺序或临时节点,若不指定,则表示持 久节点;acl 用来进行权限控制。

 

建立顺序节点:

建立永久节点:

读取节点

 

与读取相关的命令有 ls  命令和 get  命令,ls 命令能够列出 Zookeeper 指

 

定节点下的全部子节点,只能查看指定节点下的第一级的全部子节点;get 命令

 

能够获取 Zookeeper 指定节点的数据内容和属性信息。 ls path [watch]

 

get path [watch] ls2 path [watch]

 

set path data [version]

 

data 就是要更新的新内容,version 表示数据版本。

如今 dataVersion 已经变为 1 了,表示进行了更新。

删除节点

delete path [version]

若删除节点存在子节点,那么没法删除该节点,必须先删除子节点,再删除父节点。

Rmr path

能够递归删除节点。

quota

setquota -n|-b val path 对节点增长限制。 n:表示子节点的最大个数 b:表示数据值的最大长度 val:子节点最大个数或数据值的最大长度 path:节点路径

 

listquota path        列出指定节点的 quota

子节点个数为 2,数据长度-1 表示没限制 

delquota [-n|-b] path 删除 quota

 

其余命令

history : 列出命令历史

redo:该命令能够从新执行指定命令编号的历史命令,命令编号能够经过

 

history 查看

 

2.五、zookeeper的数据模型

ZooKeeper 的数据模型,在结构上和标准文件系统的很是类似,拥有一个层

 

次的命名空间,都是采用树形层次结构,ZooKeeper 树中的每一个节点被称为—

 

Znode。和文件系统的目录树同样,ZooKeeper 树中的每一个节点能够拥有子节点。

 

但也有不一样之处:

 

  1. Znode 兼具文件和目录两种特色。既像文件同样维护着数据、元信息、ACL、 时间戳等数据结构,又像目录同样能够做为路径标识的一部分,并能够具备 子 Znode。用户对 Znode 具备增、删、改、查等操做(权限容许的状况下)。

 

  1. Znode 具备原子性操做,读操做将获取与节点相关的全部数据,写操做也将 替换掉节点的全部数据。另外,每个节点都拥有本身的 ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点能够执行的操做。

 

  1. Znode 存储数据大小有限制。ZooKeeper 虽然能够关联一些数据,但并无 被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据, 好比分布式应用中的配置文件信息、状态信息、聚集位置等等。这些数据的 共同特性就是它们都是很小的数据,一般以 KB 为大小单位。ZooKeeper 的服 务器和客户端都被设计为严格检查并限制每一个 Znode 的数据大小至多 1M,常规使用中应该远小于此值。

 

  1. Znode 经过路径引用,如同 Unix 中的文件路径。路径必须是绝对的,所以他 们必须由斜杠字符来开头。除此之外,他们必须是惟一的,也就是说每个 路径只有一个表示,所以这些路径不能改变。在 ZooKeeper 中,路径由 Unicode 字符串组成,而且有一些限制。字符串"/zookeeper"用以保存管理 信息,好比关键配额信息。

 

2.5.一、数据结构

图中的每一个节点称为一个 Znode。 每一个 Znode 由 3 部分组成:

 

① stat:此为状态信息, 描述该 Znode 的版本, 权限等信息

 

② data:与该 Znode 关联的数据

 

③ children:该 Znode 下的子节点

2.5.二、节点类型

Znode 有两种,分别为临时节点和永久节点。

 

节点的类型在建立时即被肯定,而且不能改变。

 

临时节点:该节点的生命周期依赖于建立它们的会话。一旦会话结束,临时 节点将被自动删除,固然能够也能够手动删除。临时节点不容许拥有子节点。

 

永久节点:该节点的生命周期不依赖于会话,而且只有在客户端显示执行删

 

除操做的时候,他们才能被删除。

 

Znode 还有一个序列化的特性,若是建立的时候指定的话,该 Znode 的名字

 

后面会自动追加一个不断增长的序列号。序列号对于此节点的父节点来讲是惟一

 

的,这样便会记录每一个子节点建立的前后顺序。它的格式为“%10d”(10 位数字,

 

没有数值的数位用 0 补充,例如“0000000001”)。

 

这样便会存在四种类型的 Znode 节点,分别对应:

 

PERSISTENT:永久节点

 

EPHEMERAL:临时节点

 

PERSISTENT_SEQUENTIAL:永久节点、序列化

 

EPHEMERAL_SEQUENTIAL:临时节点、序列化

 

建立永久节点:

[zk: localhost:2181(CONNECTED) 3] create /hello world

Created /hello

建立临时节点:

[zk: localhost:2181(CONNECTED) 5] create -e /abc 123

Created /abc

建立永久序列化节点:

[zk: localhost:2181(CONNECTED) 6] create -s /zhangsan boy

Created /zhangsan0000000004

建立临时序列化节点:

zk: localhost:2181(CONNECTED) 11] create -e -s /lisi boy

Created /lisi0000000006

2.5.三、节点属性

每一个 znode 都包含了一系列的属性,经过命令 get,能够得到节点的属性。

 

dataVersion:数据版本号,每次对节点进行 set 操做,dataVersion 的值都

 

会增长 1(即便设置的是相同的数据),可有效避免了数据更新时出现的前后顺

 

序问题。

 

cversion :子节点的版本号。当 znode 的子节点有变化时,cversion 的值

 

就会增长 1。

 

aclVersion :ACL 的版本号。

 

cZxid :Znode 建立的事务 id。

 

mZxid  :Znode 被修改的事务 id,即每次对 znode 的修改都会更新 mZxid。

 

对于 zk 来讲,每次的变化都会产生一个惟一的事务 id,zxid(ZooKeeper Transaction Id)。经过 zxid,能够肯定更新操做的前后顺序。例如,若是 zxid1

 

小于 zxid2,说明 zxid1 操做先于 zxid2 发生,zxid 对于整个 zk 都是惟一的,

 

即便操做的是不一样的 znode。

 

ctime:节点建立时的时间戳.

 

mtime:节点最新一次更新发生时的时间戳.

 

ephemeralOwner:若是该节点为临时节点, ephemeralOwner 值表示与该节点

 

绑定的 session id. 若是不是, ephemeralOwner 值为 0.

 

在 client 和 server 通讯以前,首先须要创建链接,该链接称为 session。连

 

接创建后,若是发生链接超时、受权失败,或者显式关闭链接,链接便处于 CLOSED

状态, 此时 session 结束。

2.六、zookeeper的watch机制

ZooKeeper 提供了分布式数据发布/订阅功能,一个典型的发布/订阅模型系

 

统定义了一种一对多的订阅关系,能让多个订阅者同时监听某一个主题对象,当

 

这个主题对象自身状态变化时,会通知全部订阅者,使他们可以作出相应的处理。

 

ZooKeeper 中,引入了 Watcher 机制来实现这种分布式的通知功能 。

 

ZooKeeper 容许客户端向服务端注册一个 Watcher 监听,当服务端的一些事件触

 

发了这个 Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。

 

触发事件种类不少,如:节点建立,节点删除,节点改变,子节点改变等。

 

总的来讲能够归纳 Watcher 为如下三个过程:客户端向服务端注册 Watcher、

 

服务端事件发生触发 Watcher、客户端回调 Watcher 获得触发事件状况

2.6.一、watch机制特色

一次性触发

 

事件发生触发监听,一个 watcher event 就会被发送到设置监听的客户端,

 

这种效果是一次性的,后续再次发生一样的事件,不会再次触发。

 

事件封装

 

ZooKeeper 使用 WatchedEvent 对象来封装服务端事件并传递。

 

WatchedEvent 包含了每个事件的三个基本属性:

 

通知状态(keeperState),事件类型(EventType)和节点路径(path)

 

event 异步发送

 

watcher 的通知事件从服务端发送到客户端是异步的。

 

先注册再触发

 

Zookeeper 中的 watch 机制,必须客户端先去服务端注册监听,这样事件发

 

送才会触发监听,通知给客户端。

 

2.6.二、通知状态和事件类型

同一个事件类型在不一样的通知状态中表明的含义有所不一样,下表列举了常见

 

的通知状态和事件类型。

KeeperState

EventType

触发条件

说明

 

None

(-1)

客户端与服务端成功创建链接

 

SyncConnected

(0)

NodeCreated

(1)

Watcher 监听的对应数据节点被建立

 

NodeDeleted

(2)

Watcher 监听的对应数据节点被删除

此时客户端和服务器处于链接状态

NodeDataChanged

3

Watcher 监听的对应数据节点的数据内容 发生变动

 

NodeChildChanged

(4)

Wather 监听的对应节点的子节点数据列表发生变动

 

 

Disconnected

(0)

None

(-1)

客户端与zookeeper服务器断开链接

此时客户端和服务器处于断开链接状态

Expired

(-112)

Node

(-1)

会话超时

此时客户端会话失效,一般同时也会收到SessionExpiredException 异常

AuthFailed

(4)

None

(-1)

一般有两种状况

1:使用错误的

schema 进行权限检查

2:SASL 权限检查失败

一般同时也会收到AuthFailedException 异常

 

其中链接状态事件(type=None, path=null)不须要客户端注册,客户端只要

 

有须要直接处理就好了。

 

 

2.6.三、shell客户端设置watch机制

设置节点数据变更监听:

 

经过另外一个客户端更改节点数据:

 

此时设置监听的节点收到通知:

 

 

2.七、zookeeper的javaAPI

Zookeeper 是在 Java 中客户端主类,负责创建与 zookeeper 集群的会话,并提供方法进行操做。org.apache.zookeeper.Watcher

Watcher 接口表示一个标准的事件处理器,其定义了事件通知相关的逻辑,

 

包含 KeeperState 和 EventType 两个枚举类,分别表明了通知状态和事件类型,

 

同时定义了事件的回调方法:process(WatchedEvent event)。

 

process 方法是 Watcher 接口中的一个回调方法,当 ZooKeeper 向客户端发

 

送一个 Watcher 事件通知时,客户端就会对相应的 process 方法进行回调,从而

 

实现对事件的处理。

 

2.7.一、建立java工程,导入jar包

建立maven  java工程,导入jar包

<!-- <repositories>

    <repository>

      <id>cloudera</id>

      <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>

    </repository>

  </repositories> -->

   <dependencies>

       <dependency>

            <groupId>org.apache.curator</groupId>

            <artifactId>curator-framework</artifactId>

            <version>2.12.0</version>

        </dependency>

        <dependency>

            <groupId>org.apache.curator</groupId>

            <artifactId>curator-recipes</artifactId>

            <version>2.12.0</version>

        </dependency> 

       <dependency>

           <groupId>com.google.collections</groupId>

           <artifactId>google-collections</artifactId>

           <version>1.0</version>

       </dependency>

  </dependencies>

  <build>

       <plugins>

           <!-- java编译插件 -->

           <plugin>

              <groupId>org.apache.maven.plugins</groupId>

              <artifactId>maven-compiler-plugin</artifactId>

              <version>3.2</version>

              <configuration>

                  <source>1.8</source>

                  <target>1.8</target>

                  <encoding>UTF-8</encoding>

              </configuration>

           </plugin>

       </plugins>

    </build>

 

2.7.二、节点的操做

建立永久节点

/**

     * 建立永久节点

     * @throws Exception

     */

    @Test

    public void createNode() throws Exception {

      RetryPolicy retryPolicy = new  ExponentialBackoffRetry(1000, 1);

//获取客户端对象

       CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.221.100:2181,192.168.221.110:2181,192.168.221.120:2181", 1000, 1000, retryPolicy);

//调用start开启客户端操做

       client.start();

    //经过create来进行建立节点,而且须要指定节点类型

client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/hello3/world");

client.close();

    }

建立临时节点

    /**

     * 建立临时节点

     * @throws Exception

     */

    @Test

    public void createNode2() throws Exception {

      RetryPolicy retryPolicy = new  ExponentialBackoffRetry(3000, 1);

       CuratorFramework client = CuratorFrameworkFactory.newClient("node01:2181,node02:2181,node03:2181", 3000, 3000, retryPolicy);

       client.start();

client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/hello5/world");

       Thread.sleep(5000);

       client.close();

    }

   

修改节点数据

   

    /**

     * 节点下面添加数据与修改是相似的,一个节点下面会有一个数据,新的数据会覆盖旧的数据

     * @throws Exception

     */

    @Test

    public void nodeData() throws Exception {

      RetryPolicy retryPolicy = new  ExponentialBackoffRetry(3000, 1);

       CuratorFramework client = CuratorFrameworkFactory.newClient("node01:2181,node02:2181,node03:2181", 3000, 3000, retryPolicy);

       client.start();

       client.setData().forPath("/hello5", "hello7".getBytes());

       client.close();

    }

节点数据查询

    /**

     * 数据查询

     */

    @Test

    public void updateNode() throws Exception {

      RetryPolicy retryPolicy = new  ExponentialBackoffRetry(3000, 1);

       CuratorFramework client = CuratorFrameworkFactory.newClient("node01:2181,node02:2181,node03:2181", 3000, 3000, retryPolicy);

       client.start();

       byte[] forPath = client.getData().forPath("/hello5");

       System.out.println(new String(forPath));

       client.close();

    }

   

   

节点watch机制

    /**

     * zookeeper的watch机制

     * @throws Exception

     */

    @Test

    public void watchNode() throws Exception {

       RetryPolicy policy = new ExponentialBackoffRetry(3000, 3);

       CuratorFramework client = CuratorFrameworkFactory.newClient("node01:2181,node02:2181,node03:2181", policy);

       client.start();

       // ExecutorService pool = Executors.newCachedThreadPool(); 

            //设置节点的cache 

            TreeCache treeCache = new TreeCache(client, "/hello5"); 

            //设置监听器和处理过程 

            treeCache.getListenable().addListener(new TreeCacheListener() { 

                @Override 

                public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { 

                    ChildData data = event.getData(); 

                    if(data !=null){ 

                        switch (event.getType()) {

                        case NODE_ADDED

                            System.out.println("NODE_ADDED : "+ data.getPath() +"  数据:"+ new String(data.getData())); 

                            break

                        case NODE_REMOVED

                            System.out.println("NODE_REMOVED : "+ data.getPath() +"  数据:"+ new String(data.getData())); 

                            break

                        case NODE_UPDATED

                            System.out.println("NODE_UPDATED : "+ data.getPath() +"  数据:"+ new String(data.getData())); 

                            break

                             

                        default

                            break

                        } 

                    }else

                        System.out.println( "data is null : "+ event.getType()); 

                    } 

                } 

            }); 

            //开始监听 

            treeCache.start(); 

            Thread.sleep(50000000);

    }

相关文章
相关标签/搜索