请选择 进入手机版 | 继续访问电脑版
JAVEN

ZooKeeper详解

所在版块: JavaEE技术 2017-09-27 15:40 [复制链接] 查看: 530|回复: 1
二十、ZooKeeper详解
ZooKeeper是一个高可用的分布式数据管理与系统协调框架。基于对Paxos算法的实现,使该框架保证了分布式环境中数据的强一致性。因此,ZooKeeper框架的重点在“一致性”,提到一致性,会想到事务的ACID中的C(一致性)。
1、分布式事务保持一致性
在项目部署在一台服务器上提供服务的时候,我们经常会遇到如下问题:
a、访问量上升之后,一台服务器不能给予所有的请求予以响应。
b、担心这一台服务器停机了,会造成服务器不可用或者导致数据丢失。
以上就是“单点故障问题”,解决此问题的方式就是增加多台服务器分担访问量,这样也可以解决一台服务器停机之后不能提供服务的“单点故障问题”。增加机器之后的策略分两种:
a、镜像:将相同的服务和数据复制到多台机器上。(缺点:数据冗余很大,只要更新一台机器的配置,所有镜像机器上的配置都需要更新,工作量大,也存在风险----比如有一台机器忘记更新了(数据一致性风险))。
b、分区:将服务拆解成单独模块,不同的模块部署在不同机器上。(缺点:还是会存在局部单点故障导致的数据丢失。虽然不是所有模块都不可用,但是只要有一个模块不能用,整个系统也就不能正常提供服务,从而导致数据丢失(数据一致性被破坏))
如果对上面的描述还不是很明白,我们举一个通俗易懂的例子:A转账给B的经典事务控制。在分布式系统中,如果A账户数据在服务器A上,B账户数据在服务器B上,意味着要完成转账操作,需要将数据从服务器A上做减法之后到服务器B上做加法(A->B,   A-1000 则 B+1000)。如果此时B服务器出现异常,无法完成整个转账流程,则需要同时回滚A服务器上的数据操作。这个过程如果在一个进程内是比较容易操作的,但是现在跨进程(跨服务器)就很难了了,使用传统的方法是无法做到的。这时利用ZooKeeper的一致性的特点就能解决此问题。
2、一致性的分类
1、弱一致性
2、最终一致性
3、强一致性
3、ZooKeeper的实际应用场景
a、主从分区,实现读写分离之后,使用ZooKeeper实现主从服务器上的数据同步(强一致性)。
b、Nginx实现负载均衡,以单台服务器作为反向代理服务器,使用ZooKeeper解决单点故障问题,以及多个镜像服务器实现数据一致性。
以上是ZooKeeper的抽象一点的用法,更具体使用场景如下:
1、数据的发布与订阅
2、负载均衡
3、命名服务
4、分布式通知/协调
5、集群管理和Master选举
6、分布是同步锁
7、分布式队列
...
4、ZooKeeper基本模型4.1、ZooKeeper数据模型类似Linux的文件系统节点模型,如下图:
ZooKeeper的节点有如下重要特性:
  • 同一时刻多台机器创建同一个节点,只有一个会争抢成功。利用这个特性可以做分布式锁。
  • 临时节点的生命周期与会话一致,会话关闭则临时节点删除。这个特性经常用来做心跳,动态监控,负载等动作。
  • 顺序节点保证节点名全局唯一。这个特性可以用来生成分布式环境下的全局自增长id。

4.2、ZooKeeper提供的原始服务
  • 创建节点
  • 删除节点
  • 更新节点
  • 获取节点信息
  • 权限控制
  • 事件监听

这些基本功能随机组合,可以使用多种场景下的使用(场景详见《3、ZooKeeper的实际使用场景》)。除了上面列举的使用场景之外,你也可以发挥自己的想象组合原始服务扩充到更多的应用场景。
5、ZooKeeper的安装和配置5.1、单机版配置
步骤:
a、下载ZooKeeper安装文件(下载地址);
b、解压,进入conf文件夹,将zoo_sample.cfg重命名为zoo.cfg;文件内容如下:

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
dataDir=D:/zookeeper/data
dataLogDir=D:/zookeeper/logs
# the port at which the clients will connect
clientPort=2181
c、启动。打开CMD,进入ZooKeeper安装目录下的bin文件夹,输入如下启动命令:

zkServer.cmd
出现如下信息,表示启动完成:

2017-09-20 11:08:03,180 - INFO  [main:Environment@97] - Server environment:java.io.tmpdir=C:\Users\yangw\AppData\Local\Temp\
2017-09-20 11:08:03,180 - INFO  [main:Environment@97] - Server environment:java.compiler=<NA>
2017-09-20 11:08:03,195 - INFO  [main:Environment@97] - Server environments.name=Windows 10
2017-09-20 11:08:03,195 - INFO  [main:Environment@97] - Server environments.arch=amd64
2017-09-20 11:08:03,195 - INFO  [main:Environment@97] - Server environments.version=10.0
2017-09-20 11:08:03,195 - INFO  [main:Environment@97] - Server environment:user.name=yangjw
2017-09-20 11:08:03,195 - INFO  [main:Environment@97] - Server environment:user.home=C:\Users\yangw
2017-09-20 11:08:03,195 - INFO  [main:Environment@97] - Server environment:user.dir=F:\zookeeper-3.3.6\bin5.2、集群环境配置
需要准备多台机器,暂时命名为server1、server2、server3,此3台服务器组成一个机群。在三台机器上,都进行ZooKeeper的解压和配置。
5.2.1、创建myid
在在dataDir(/usr/local/zk/data)目录创建myid文件
Server0机器的内容为:0Server1机器的内容为:1Server2机器的内容为:2
5.2.2、修改配置文件
在每一个服务器上的ZooKeeper安装文件夹中进入conf文件夹,将zoo_sample.cfg重命名为zoo.cfg;文件内容如下:

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
dataDir=/tmp/zookeeper
# the port at which the clients will connect
clientPort=2181

server.1=server1-IP:2888:3888                     
server.2=server2-IP:2888:3888                     
server.3=server3-IP:2888:3888
配置说明:
(1) initLimit
此配置表示,允许follower(相对于Leaderer言的“客户端”)连接并同步到Leader的初始化连接时间,以tickTime为单位。当初始化连接时间超过该值,则表示连接失败。
(2) syncLimit
此配置项表示Leader与Follower之间发送消息时,请求和应答时间长度。如果follower在设置时间内不能与leader通信,那么此follower将会被丢弃。
(3) server.A=B:C:D
A:其中 A 是一个数字,表示这个是服务器的编号;B:是这个服务器的 ip 地址;C:Leader选举的端口;D:Zookeeper服务器之间的通信端口。
(4) myidzoo.cfg
除了修改 zoo.cfg 配置文件,集群模式下还要配置一个文件 myid,这个文件在 dataDir 目录下,这个文件里面就有一个数据就是 A 的值,Zookeeper 启动时会读取这个文件,拿到里面的数据与 zoo.cfg 里面的配置信息比较从而判断到底是那个 server。
5.2.3、启动
分别在三台服务器上执行如下命令:

zkServer.cmd
6、ZooKeeper的命令操作
上面配置步骤已经启动了ZooKeeper的服务端,接着我们需要开启ZooKeeper的客户端对ZooKeeper进行操作,命令如下:

zkCli.cmd -server localhost:2181
连接成功之后会出现如下信息:

ZooKeeper -server host:port cmd args
        stat path [watch]
        set path data [version]
        ls path [watch]
        delquota [-n|-b] path
        ls2 path [watch]
        setAcl path acl
        setquota -n|-b val path
        history
        redo cmdno
        printwatches on|off
        delete path [version]
        sync path
        listquota path
        get path [watch]
        create [-s] [-e] path data acl
        addauth scheme auth
        quit
        getAcl path
        close
        connect host:port
之后,我们就可以开始通过命令操作ZooKeeper了。
(1) 使用ls命令查看当前Zookeeper中所包含的内容:ls /

[zk: localhost:2181(CONNECTED) 1] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 2]
(2) 创建一个新的Znode节点"zk",以及和它相关字符,执行命令:create /zk myData

[zk: localhost:2181(CONNECTED) 2] create /zk myData
Created /zk
(3) 再次使用ls命令来查看现在Zookeeper的中所包含的内容:ls /

[zk: localhost:2181(CONNECTED) 3] ls /
[zk, zookeeper]
此时看到,zk节点已经被创建。  
(4) 使用get命令来确认第二步中所创建的Znode是否包含我们创建的字符串,执行命令:get /zk

[zk: localhost:2181(CONNECTED) 4] get /zk
myData
cZxid = 0x500000006
ctime = Fri Oct 17 03:54:20 PDT 2014
mZxid = 0x500000006
mtime = Fri Oct 17 03:54:20 PDT 2014
pZxid = 0x500000006
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
(5) 接下来通过set命令来对zk所关联的字符串进行设置,执行命令:set /zk jiang1234

[zk: localhost:2181(CONNECTED) 5] set /zk jiang2014
cZxid = 0x500000006
ctime = Fri Oct 17 03:54:20 PDT 2014
mZxid = 0x500000007
mtime = Fri Oct 17 03:55:50 PDT 2014
pZxid = 0x500000006
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 0
(6) 再次使用get命令来查看,上次修改的内容,执行命令:get /zk

[zk: localhost:2181(CONNECTED) 6] get /zk
jiang2014
cZxid = 0x500000006
ctime = Fri Oct 17 03:54:20 PDT 2014
mZxid = 0x500000007
mtime = Fri Oct 17 03:55:50 PDT 2014
pZxid = 0x500000006
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 0
(7) 下面我们将刚才创建的Znode删除,执行命令:delete /zk

[zk: localhost:2181(CONNECTED) 7] delete /zk
(8) 最后再次使用ls命令查看Zookeeper中的内容,执行命令:ls /

[zk: localhost:2181(CONNECTED) 8] ls /
[zookeeper]
经过验证,zk节点已经删除。
7、ZooKeeper Java API的基本使用7.1、导包
<dependency>
  <groupId>org.apache.zookeeper</groupId>
  <artifactId>zookeeper</artifactId>
  <version>3.4.10</version>
</dependency>7.2、连接ZooKeeper的服务器
//参数1:zookeeper服务器的IP地址和端口号
//参数2:zookeeper的session失效时间
//参数3:观察者(一个监听器,用来监听zookeeper的变化)
ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2181", 50000, new Watcher() {
                public void process(WatchedEvent watchedEvent) {
                    System.out.println("------触发了------" + watchedEvent.getType() + "事件----" + watchedEvent.getPath());
                }
            });7.3、增删改查7.3.1、添加节点
//参数1:节点名称
//参数2:节点数据内容
//参数3:访问控制权限列表,OPEN_ACL_UNSAFE 表示开放所有权限
zooKeeper.create("/demo","javen".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
ZooKeeper的权限如下:
  • CREATE: 能创建子节点
  • READ:能获取节点数据和列出其子节点
  • WRITE: 能设置节点数据
  • DELETE: 能删除子节点
  • ADMIN: 能设置权限

7.3.2、删除节点
//参数1:节点名称
//参数2:版本号,-1表示删除所有版本
zooKeeper.delete("/demo",-1);7.3.3、修改节点
//参数1:节点名称
//参数2:内容
//参数3:版本号,-1表示修改所有版本
zooKeeper.setData("/demo","haha".getBytes(),1);7.3.4、查询节点
//参数1:节点名称
//参数2:是否监听节点的变化
//参数3:
zooKeeper.getData("/demo",true, null);7.3.5、完整示例代码
package com.javen.zookeeper.demo;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;

public class ZookeeperDemo {

    public static void main(String[] args) {

        try {
            ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2181", 50000, new Watcher() {
                public void process(WatchedEvent watchedEvent) {
                    System.out.println("------触发了------" + watchedEvent.getType() + "事件----" + watchedEvent.getPath());
                }
            });
            //判断节点是否存在,如果存在,则监听此节点
            zooKeeper.exists("/demo",true);
            //添加节点
            zooKeeper.create("/demo","javen".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
            //查询节点数据
            byte[] badd = zooKeeper.getData("/demo",true, null);
            System.out.println(new String(badd));
            //修改节点
            zooKeeper.setData("/demo","haha".getBytes(),1);
            byte[] bupdate = zooKeeper.getData("/demo",true, null);
            System.out.println(new String(bupdate));
            //删除节点
            zooKeeper.delete("/demo",-1);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }
}8、
回复

使用道具 举报

smlqf7

该用户从未签到

发表于 2018-7-26 12:08:57 | 显示全部楼层
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我的博客

QQ|Archiver|手机版|小黑屋|课堂笔记  

GMT+8, 2018-12-12 10:55 , Processed in 0.087108 second(s), 28 queries .

快速回复 返回列表