Zookeeper介绍

 最近工作时间很忙,写博客的时间也少了,说白点偷懒了一下,最近在看视频的时候复习了一下Zookeeper,之前在CSDN也分享过,并详细整理一下Zookeeper学习的流程笔记:

          http://blog.csdn.net/tang06211015/article/details/51842709

  如果想深入学习Zookeeper ,建议买一本:<<从Paxos到ZooKeeper 分布式一致性原理与实践>> 书籍;

 

接下来的系列也是以Zookeeper为主: 

大概的系列纲要为:

  zookeeper 简介

  Zookeeper 基本概念:

  zookeeper 部署

  zookeeper 的使用(原生API、 ZKCLinet 、curator)

  zookeeper 的应用场景分析

  zookeeper 技术内幕的分析;

  Zookeeper 与Paxos:

 

   其实Zookeeper并没有想象中的多么复杂,复杂的是在项目中怎么去应用Zookeeper,那些场景需要使用Zookeeper, 这就考验公司架构师的功底了,闲话不多说:

 

Zookeeper介绍

      它是一个针对大型分布式系统的可靠协调系统,提供的功能包括:配置维护、名字服务、分布式同步、组服务等。ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。

Zookeeper 特性:

    顺序一致性:从同一个客户端发起的事务请求,最终将严格按照其发起顺序被应用到ZooKeeper中。

    原子性:更新操作要么成功要么失败,没有中间状态

    单一视图:不管客户端连接哪一个服务器,客户端看到服务端的数据模型都是一致的(the same view of service)。

    可靠性:一旦一个更新成功,那么那就会被持久化,直到客户端用新的更新覆盖这个更新。

    实时性:Zookeeper仅保证在一定时间内,客户端最终一定能够从服务端读到最新的数据状态

Zookeeper设计目标

   简单的数据模型:

     Zookeeper使得分布式程序能够通过一个共享的、树形结构的名字空间来进行相互协调它采用了类似文件系统的目录树型结构的数据模型。协调服务难于处理,特别容易出错,比如条件竞争和死锁。ZooKeeper的动机是为了减轻分布式应用实现协调服务的负担。zookeeper允许分布式应用通过共享的层次化名字空间进行相互协调。zookeeper在内存中维护数据,访问上具备高吞吐、低延迟的特点。zookeeper重视高性能、高可用、严格有序存取,因此,性能方面能够胜任大型分布式系统,可靠方面可屏蔽单点故障,严格有序存取确保客户端能够实现复杂的同步原语。

   如下图:Zookeeper数据结构

  blob.png

  构建集群:

    一个Zookeeper集群通常由一组机器组成,一般由3~5台就可以组成一个可用的Zookeeper集群,如图所示:

blob.png

   组成Zookeeper的集群的的每台机器都在内存中维护当前服务器的状态,并且每台机器之间都互相保持着通信,过半机器能够正常工作,集群就能对外提供服务。Zookeeper的client会选择集群内任意一台机器创建TCP连接。

  顺序访问:

     对于来自客户端的每个更新请求,zookeeper会分配一个全局唯一的递增编号,这个编号反应了所有事物的操作先后顺序,应用程序可以基于此实现更高层次的同步原语。

  高性能:

     数据都在内存中存放,适合以读为主的场景。


Zookeeper的基本概念:

      介绍一些zookeeper的基本概念。包括:集群角色、会话、数据节点、版本、watcher、ACL权限控制。

   blob.png

集群角色 

         Zookeeper集群中一共有三种角色,分别是:Leader、Follower,Observer。Leader服务器是整个zookeeper集群工作机制中的核心,Follower服务器是Zookeeper集群状态的跟随着,Observer服务器充当一个观察这的角色。

 会话

        Session会话是指客户端会话,在Zookeeper中,一个客户端链接是指客户端与服务端之间的一个TCP链接,客户端在启动的时候首先会与服务器建立一个TCP连接,通过这个链接,客户端能够通过心跳检测与服务器保持有效的会话,也能向Zookeeper服务器发送请求并获得响应。同时还能够通过该链接接收来至服务器的Wather事件通知,sessionTimeout值来设置一个客户端会话的超时时间。当由于服务器压力过大,网络故障,客户端断开等各种原因导致客户端链接断开时,只要在sessionTimeout值范围内,其客户端的会话任然有效。

 数据节点:

      Zookeeper节点分成两类:一类指集群中的机器,称为机器节点。第二类则是指数据模型中的数据单元-ZNode.。

      Znode分为持久化节点和临时节点,持久化节点一致保存在Zookeeper上,临时节点的生命周期和客户端会话绑定,客户端会话失效,则临时节点就会被移除。Zookeeper还允许用户为每一个节点添加一个特殊属性:SEQUENTIAL(就是Zookeeper会自动在器节点后面加上一个整型数字,自增的数字),称为顺序节点。

 版本:

   Zookeeper会为每个Znode维护一个叫作Stat的数据结构,结构如图:

blob.png

 注:cZxid表示创建该节点时候的zxid,mZxid表示当前的,zxid是用来为选举leader服务的,

 

 Wather

     时间监听器,Zookeeper允许用户在指定节点上注册一些Watcher,当数据节点发生变化的时候,Zookeeper服务器会把这个变化的通知发送给监听的客户端。

 

   1.一次性触发器

        lient在一个节点上设置watch,随后节点内容改变,client将获取事件。当节点内容再次改变,client不会获取这个事件,除非它又执行了一次读操作并设置watch

   2.发送至client,watch事件延迟

        watch事件异步发送至观察者。比如说client执行一次写操作,节点数据内容发生变化,操作返回后,而watch事件可能还在发往client的路上。这种情况下,zookeeper提供有序保证:client不会得知数据变化,直到它获取watch事件。网络延迟或其他因素可能导致不同client在不同时刻获取watch事件和操作返回值。

   3.设置watch的数据内容

        涉及到节点改变的不同方式。比方说zookeeper维护两个watch列表:节点的数据watch和子节点watch。getData()和exists()设置了内容watch,getChildren()设置了子节点watch,操作返回的数据类型不同,前者是节点的内容,后者是节点的子节点列表。setData()触发内容watch,create()触发当前节点的"内容watch"和其父节点的"子节点watch",delete()同时触发"内容watch"和"子节点watch"(其子节点被全部删除),以及其父节点的"子节点watch"。说白了,对当前节点的操作,要考虑到对其父节点与子节点的影响。

    watch在客户端所连接的服务端本地维护。watch的设置、维护、分发操作都很轻量级。当客户端连接到新的服务端,watch将被任一会话事件触发。与服务端断开连接时,不能获取watch事件。客户端重连后,之前注册的watch将被重新注册并在需要时触发。通常这一切透明地发生,用户不会察觉到。有一种情况watch可能丢失:之前对一个尚未建立的节点的设置了exists watch,如果断开期间该节点被建立或删除,那么此watch将丢失。

 

对于watch,zookeeper提供以下保证:

    1.watch对于其他事件、watch、异步响应是有序的。zookeeper client library保证有序分发

    2.客户端监视一个节点,总是先获取watch事件,再发现节点的数据变化。

    3.watch事件的顺序对应于zookeeper服务所见的数据更新的顺序。

关于watch要记住的是:

    1.watch是一次性触发的,如果获取一个watch事件并希望得到新变化的通知,需要重新设置watch

    2.watch是一次性触发的并且在获取watch事件和设置新watch事件之间有延迟,所以不能可靠的观察到节点的每一次变化。要认识到这一点。

    3.watch object只触发一次,比如,一个watch object被注册到同一个节点的getData()和exists(),节点被删除,仅对应于exists()的watch ojbect被调用

    4.若与服务端断开连接,直到重连后才能获取watch事件。

Watch事件类型:

    ZOO_CREATED_EVENT:节点创建事件,需要watch一个不存在的节点,当节点被创建时触发,此watch通过zoo_exists()设置

    ZOO_DELETED_EVENT:节点删除事件,此watch通过zoo_exists()或zoo_get()设置

    ZOO_CHANGED_EVENT:节点数据改变事件,此watch通过zoo_exists()或zoo_get()设置

    ZOO_CHILD_EVENT:子节点列表改变事件,此watch通过zoo_get_children()或zoo_get_children2()设置

    ZOO_SESSION_EVENT:会话失效事件,客户端与服务端断开或重连时触发

    ZOO_NOTWATCHING_EVENT:watch移除事件,服务端出于某些原因不再为客户端watch节点时触发

实例代码:

public void process(WatchedEvent event) {
        // 连接状态
        KeeperState keeperState = event.getState();
        // 事件类型
        EventType eventType = event.getType();
        // 受影响的path
        String path = event.getPath();
        String logPrefix = "【Watcher-" + this.seq.incrementAndGet() + "】";
        System.out.println(logPrefix + "收到Watcher通知");
        System.out.println(logPrefix + "连接状态:\t" + keeperState.toString());
        System.out.println(logPrefix + "事件类型:\t" + eventType.toString());
        if (KeeperState.SyncConnected == keeperState) {
 
            // 成功连接上ZK服务器
            if (EventType.None == eventType) {
                System.out.println(logPrefix + "成功连接上ZK服务器");
            }
            // 创建节点
            else if (EventType.NodeCreated == eventType) {
                System.out.println(logPrefix + "节点创建");
            }
            // 更新节点
            else if (EventType.NodeDataChanged == eventType) {
                System.out.println(logPrefix + "节点数据更新");
            }
            // 更新子节点
            else if (EventType.NodeChildrenChanged == eventType) {
                System.out.println(logPrefix + "子节点变更");
            }
            // 删除节点
            else if (EventType.NodeDeleted == eventType) {
                System.out.println(logPrefix + "节点 " + path + " 被删除");
            } else
                ;
        } else if (KeeperState.Disconnected == keeperState) {
            System.out.println(logPrefix + "与ZK服务器断开连接");
        } else if (KeeperState.AuthFailed == keeperState) {
            System.out.println(logPrefix + "权限检查失败");
        } else if (KeeperState.Expired == keeperState) {
            System.out.println(logPrefix + "会话失效");
        } else
            ;
        System.out.println("--------------------------------------------");
 
    }

 ACL权限控制:

    这个我很少用到,常用的都是基于digest方式,

   认证类型:

      WORLD("world"),AUTH("auth"),IP("ip"),DIGEST("digest");

   权限:

     CREATE:穿件子节点的权限  READ:获取节点数据和子节点列表的权限   WRITE:更新节点数据的权限

     DELETE:删除子节点的权限   ADMIN:设置节点ACL的权限

blob.png 

发表评论