ZooKeeper

ZooKeeper

1.     ZooKeeper入门

1.1.  教学目标

1.1.1.  掌握ZooKeeper的是什么

1.1.2.  掌握ZooKeeper的角色

1.1.3. 掌握ZooKeeper的数据模型和节点

1.1.4.  掌握ZooKeeper的单机和集群安装

1.1.5.  掌握ZooKeeper的基本操作

1.1.6. 了解ZooKeeper的应用场景

1.2.  课程内容

1.2.1. ZooKeeper是什么

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

一言以蔽之,ZooKeeper是分布式系统中的协调系统。

有如下特点:

Ø  简单

ZooKeeper的核心是一个精简的文件系统,它支持一些简单的操作和一些抽象操作,例如,排序和通知。

Ø  丰富

   ZooKeeper的原语操作是很丰富的,可实现一些协调数据结构和协议。例如,分布式队列、分布式锁和一组同级别节点中的“领导者选举”。

Ø  高可靠

ZooKeeper支持集群模式,可以很容易的解决单点故障问题。

Ø  松耦合交互

不同进程间的交互不需要了解彼此,甚至可以不必同时存在,某进程在ZooKeeper中留下消息后,该进程结束后其它进程还可以读这条消息。

Ø  资源库

   ZooKeeper实现了一个关于通用协调模式的开源共享存储库,能使开发者免于编写这类通用协议。

1.2.2. ZooKeeper的角色


领导者(leader):负责进行投票的发起和决议,更新系统状态。

学习者(learner):包括跟随者(follower)和观察者(observer),follower用于接受客户端请求并向客户端返回结果,在选举过程中参与投票。Observer可以接受客户端连接,将写请求转发给leader,但observer不参与投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度。

客户端(client),请求发起方。

 

1.2.3. ZooKeeper的数据模型和节点

1.2.3.1.     数据模型
1.2.3.2.     节点

ZooKeeper节点znode有两种类型,临时节点(ephemeral)和持久节点(persistent)。Znode的类型在创建时确定并且之后不能再修改。

ephemeral节点在客户端会话结束时,将会被zookeeper删除,并且ephemeral节点不可以有子节点。

Persistent节点不依赖与客户端会话,只有荡客户端明确要删除该persistent节点时才会被删除。

目前Znode有四种形式的目录节点,PERSISTENT、PERSISTENT_SEQUENTIAL、EPHEMERAL、EPHEMERAL_SEQUENTIAL

Znode可以是临时节点,一旦创建这个znode的客户端与服务器失去联系,这个znode也将自动删除,ZooKeeper的客户端和服务器通信采用长连接方式,每个客户端和服务器通过心跳来保持连接,这个连接状态称之为session,如果znode是临时节点,这个seesion失效,znode也就删除了;持久化目录节点,这个目录节点存储的数据不会丢失;顺序自动编号的目录节点,这种目录节点会更具当前已经存放在的节点数自动加1,然后返回给客户端已经成功创建的目录节点名;临时目录节点,一旦创建这个节点的客户端和服务器端口也就是session超时,这种节点会被自动删除。

1.2.4. ZooKeeper的安装

ZooKeeper的安装和配置十分简单,既可以配置成单机模式,也可以配置成集群模式,下面就一一介绍这几种安装方式。

官方网址:http://zookeeper.apache.org。

讲课采用的版本:zookeeper-3.4.6.tar.gz。

下载地址:

http://archive.apache.org/dist/zookeeper/zookeeper-3.4.6/zookeeper-3.4.6.tar.gz

1.2.4.1.     单机模式

将ZooKeeper的安装包解压到/usr/local/目录下。当然为了以后使用其命令的方便,可以在环境变量中配置ZOOKEEPER_HOME。

进入ZooKeeper目录下的conf子目录。

将zoo_sample.cfg备份为zoo.cfg,在次基础上进行修改配置

主要配置以下括起来的几项内容

说明:

tickTime:ZooKeeper中使用的基本时间单位毫秒值

dataDirZooKeeper数据文件,可以是任意目录。

dataLogDirZooKeeper日志文件,同样可以是任意目录,需要手动创建

clientPortZooKeeper监听client连接的端口号。

至此,zookeeper的单机模式我们已经配置好了,启动server只需要运行其脚本。

sh $ZOOKEEPER_HOME/bin/zkServer.sh start

如上图,ZooKeeper服务已经启动,这个时候可以启动ZooKeeper客户端来连接server。

sh $ZOOKEEPER_HOME/bin/zkCli.sh -serverlocalhost:2181

1.2.4.2.     伪分布模式

所谓伪分布模式,是指在单台物理机器上启动多个ZooKeeper进程,并组成一个集群。一般的ZooKeeper的节点个数都2n+1,这里以启动3个进程为例,将上述的zookeeper的目录拷贝2份。

修改zookeeper0/conf/zoo.cfg配置文件为如下,新加几项配置项。

新增参数含义如下:

initLimit:ZooKeeper集群中的包含多台server,其中一台为leader,集群中其余为follower。initLimit参数配置初始化连接时,follower和leader之间的最长心跳时间。此时该参数设置为10,说明时间限制为10倍tickTime,即10*2000=20000ms=20s。
    syncLimit:该参数配置leader和follower之间发送消息,请求和应答的最大时间长度。此时该参数设置为5,说明时间限制为5倍tickTime,即10s。

server.X=A:B:C 其中X是一个数字,表示这是第几号server,A是该server所在的IP地址,B配置该server和集群中的leader交换消息所需要使用的端口,C配置选举leader时所使用的端口。由于配置的是伪分布模式,所以各个server的B,C参数必须不同。

参照zookeeper0/conf/zoo.cfg,配置zookeeper1/conf/zoo.cfg和zookeeper2/conf/zoo.cfg文件,只需要修改dataDir、dataLogDir、clientPort

 

在之前设置的dataDir中新建一个myid文件,写入一个数字,该数字表示这是第几号server,该数字必须和server.X中的X一一对应。

/usr/local/zookeeper0/data/myid文件写入0,以此类推。

这个时候,分别进入zookeeper*/bin目录下,启动个子server

查看各个节点状态:

连接客户端和单机模式连接一样,进入任何一个节点

bin/zkCli.sh -server localhost:2181/2812/2183

1.2.4.3.     完全分布式模式

完全分布式模式和伪分布模式基本一致。由于完全分布式模式下,各个server部署在不同的机器上,因此各server的conf/zoo.cfg文件可以完全一样。

以下为一个配置示例:

其中三台机器的zoo.cfg完全一致。只不过这里的myid改为150,151,152了,需要互相保持一致。启动服务,客户端连接都和上述一致。

1.2.5. ZooKeeper的基本操作

1.2.5.1.     Shell操作

ZooKeeper相关操作如下:

   

1、连接到ZooKeeper服务

[root@service zookeeper0]# bin/zkCli.sh-server localhost:2181

2、使用ls命令查看当前ZooKeeper中包含的内容

3、创建新的znode,使用create命令

4、获取节点中的值,get命令

5、使用set命令来对znode关联的字符串进行设置

6、删除节点

1.2.5.2.     Java api操作

客户端要连接ZooKeeper服务器可以通过创建org.apache.zookeeper.ZooKeeper的一个实例对象,然后调用提供的api与服务器进行交互。

之前已经了解了ZooKeeper主要是用来维护和监控一个目录节点数中存储的数据的状态,所有我们能够操作的ZooKeeper的也和操作目录节点树大体一致,如果见一个目录节点,给某个目录节点设置数据,获取某个目录节点的所有子目录节点等等。

相关主要接口如下列表所示:

方法名称

描述

String create(final String path,

byte data[], List acl, CreateMode

createMode)

创建一个znode节点。

参数:路径、znode内容、ACL(访问控制列表)、znode创建类型

void delete(final String path, int

version)

删除一个znode节点,

参数:路径、版本号;如果版本号与znode不一致,将无法删除,是一种乐观加锁机制;如果将版本号置为-1,不回去检测版本,直接删除

Stat exists(final String path,

Watcher watcher)

判断某个znode节点是否存在

参数:路径、Watcher(监视器);当这个znode节点呗改变时,将会触发当前Watcher

Stat exists(String path, boolean

watch)

判断某个znode节点是否存在

参数:路径、并设置是否监控这个目录节点,这里的watcher是在创建ZooKeeper实例时指定的watcher

Stat setData(final String path,

byte data[], int version)

设置某个znode上的数据

参数:路径、数据、版本号;如果为-1,跳过版本检查

byte[] getData(final String path,

Watcher watcher, Stat stat)

获取某个znode上的数据

参数:路径、监视器、数据版本等信息

List getChildren(final String path, Watcher watcher)

获取某个节点下的所有子节点

参数:路径、监视器;该方法有多个重载

Znode创建类型(CreateMode)

    PERSISTENT                 持久化节点

    PERSISTENT_SEQUENTIAL        顺序自动编号持久化节点,这种节点会更具当前已存在的节点数自动加1

    EPHEMERAL                      临时节点,客户端session超时就会自动删除

EPHEMERAL_SEQUENTIAL     临时自动编号节点

Zookeeper基本操作:

public class ZooKeeperTest {

    privateZooKeeper zookeeper;

   

    /**

     * 连接zookeeper

     * @throws Exception

     */

    @Before

    public void setUp() throws Exception {

        StringconStr = "service.bigdata.jack.cn:2181";

        int sessionTimeout =30000;

        zookeeper = new ZooKeeper(conStr, sessionTimeout , new Watcher() {

            @Override

            public voidprocess(WatchedEvent event) {

                System.out.println("已经触发了" + event.getType()+ "事件!");

            }

        });

    }

   

    @Test

    public void testCRUD() throws Exception {

         // 创建一个目录节点

         zookeeper.create("/testZK", "testZKData".getBytes(),

Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

         // 创建一个子目录节点

         zookeeper.create("/testZK/childOne", "childOne".getBytes(),

Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);

         System.out.println(new

String(zookeeper.getData("/testZK",false,null)));

         // 取出子目录节点列表

         System.out.println(zookeeper.getChildren("/testZK",true));

         // 修改子目录节点数据

         zookeeper.setData("/testZK/childOne","modifyChildDataOne".getBytes(),-1);

         System.out.println("目录节点状态:

["+zookeeper.exists("/testZK",true)+"]");

         // 创建另外一个子目录节点

         zookeeper.create("/testZK/childTwo", "childTwo".getBytes(),

                   Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);

         System.out.println(new

String(zookeeper.getData("/testZK/childTwo",true,null)));

         // 删除子目录节点

         zookeeper.delete("/testZK/childTwo",-1);

         zookeeper.delete("/testZK/childOne",-1);

         // 删除父目录节点

         zookeeper.delete("/testZK",-1);

    }

    @After

    public void cleanUp() throwsInterruptedException {

        zookeeper.close();

    }

}

输出结果如下:

已经触发了None事件!

testZKData

[childOne]

目录节点状态:[73,73,1474613743218,1474613743218,0,1,0,0,10,1,74

]

已经触发了NodeChildrenChanged事件!

childTwo

已经触发了NodeDeleted事件!

已经触发了NodeDeleted事件!

当对目录节点监控状态打开时,一旦目录节点的状态发生变化,Watcher对象的process方法就会被调用。

1.2.6. ZooKeeper典型应用场景

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

下面详细介绍一下这些典型的应用场景,也就是ZooKeeper到底能帮我们解决什么问题呢?

1.2.6.1.     统一命名服务(Name Service)

分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用于树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构,即对人友好又不会重复。有点类似JNDI(Java Naming and DirectoryInterface,Java命名和目录接口,一个简单的例子就是通过配置一些xml文件,方便用户直接调用API使用某些通用的资源,如jdbc,tomcat配置),他们都是将有层次的目录结构关联到一定资源上,但是ZooKeeper的Name Service更加是广泛意义上的关联,也许你并不需要将名称关联到特定资源上,你可能只需要一个不会重复的名称,就像数据库中产生一个唯一的主键一样。

1.2.6.2.     配置管理(Configuration Management)

配置的管理在分布式应用环境中很常见,例如同一个应用系统需要多台PC Server运行,但是它们运行的应用系统的某些配置项是相同的,如果要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的PC Server,这样非常麻烦而且容易出错。

像这样的配置信息完全可以交给ZooKeeper来管理,将配置信息保存在ZooKeeper的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到ZooKeeper的通知,然后从Zookeeper获取新的配置信息引用到系统中。

图:ZooKeeper配置管理结构图

1.2.6.3.     集群管理(Group Membership)

ZooKeeper能够很容易的实现集群管理的功能,如有多台Server组成一个服务集群,那么必须要一个“总管”知道当前集群中每台机器状态,一旦有机器不能提供服务,集群中其它机器必须知道,从而做出调整重新分配服务策略。同样当增加集群的服务能力是,就会增加一台或多台Server,同样也必须让“总管”知道。

ZooKeeper不仅能够帮你维护当前的集群中的服务状态,而且能够帮你选出一个“总管”,让这个总管来管理集群,这就是ZooKeeper的另一个功能Leader Election。

他们的实现方式都是在ZooKeeper上创建一个EPHEMERAL类型的目录节点,然后每个Server在他们创建目录节点的父目录节点上调用getChildren(String path, boolean watch)方法并设置watch为true,由于是EPHEMERAL目录节点,当创建她的Server死去,这个目录节点也随之被删除,所以Children将会变化,这时getChildren上的Watch将会被调用,所以气他Server就知道已经有某台Server死去了。新增Server也是同样的道理。

ZooKeeper如何实现Leader Election,也就是选出一个Master Server。和前面的一样每台Server创建一个EPHEMERAL目录节点,不同的是它还是一个SEQUENTIAL目录节点,所以它是个EPHEMEAL_SEQUENTIAL目录节点。之所以它是EPHEMERAL_SEQUENTIAL目录节点,是因为我们可以给每台Server编号,我们可以选择当前是最小编号的Server为Master,加入这个最小编号的Server死去,由于是EPHEMERAL节点,死去的Server对应的节点也被删除,所以当前的节点列表中又出现一个最小编号的节点,我们就选择这个节点为当前Master。这样就实现了动态选择Master,避免了传统意义上单Master容易出现单点故障的问题。

集群管理结构图:

Leader Election关键代码

 

1.2.6.4.     队列管理

ZooKeeper可以处理两种类型的队列:

1、当一个队列的成员都聚齐是,这个队列才可用,否则一直等待所有成员到达,这种是同步队列。

2、队列按照FIFO方式进行入队和出队操作,列入实现生产者和消费者模型。

同步队列用ZooKeeper实现的思路如下:

创建一个父目录/synchronizing,每个成员都监控标志(Set Watch)位目录/synchronizing/start是否存在,然后每个成员都加入这个队列,加入队列的方式就是创建/synchronizing/member_i的临时目录节点,然后每个成员获取/synchronizing目录的所有目录节点,也就是member_i。判断i的值是否已经是成员的个数,如果小于成员个数等待/synchronizing/start的出现,如果已经相等就创建/synchronizing/start。

以下是流程图:

同步队列的关键代码如下,

当对列没满是进入wait(),然后会一直等待Watch的通知,Watch的代码如下:

FIFO队列用ZooKeeper实现思路如下:

就是在特定的目录下创建SEQUENTIAL类型的子目录/queue_i,这样就能保证所有成员加入队列时都是有编号的,出队列时通过getChildren()方法可以返回当前所有的队列中的元素,然后先飞其中最小的一个,这样就能保证FIFO。

下面是生产者和消费者这种队列形式的示例代码。

生产者代码

消费者代码

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值