zookeeper 简介

一、ZooKeeper是一个分布式的、提供高可用的、存放键值对的服务

分布式

Zookeeper提供了分布式独享锁
获取锁实现思路:
1.首先创建一个作为锁目录(znode),通常用它来描述锁定的实体,称为:/lock_node
2.希望获得锁的客户端在锁目录下创建znode,作为锁/lock_node的子节点,并且节点类型为有序临时节点(EPHEMERAL_SEQUENTIAL);
  例如:有两个客户端创建znode,分别为/lock_node/lock-1和/lock_node/lock-2
3.当前客户端调用getChildren(/lock_node)得到锁目录所有子节点,不设置watch,接着获取小于自己(步骤2创建)的兄弟节点
4.步骤3中获取小于自己的节点不存在 && 最小节点与步骤2中创建的相同,说明当前客户端顺序号最小,获得锁,结束。
5.客户端监视(watch)相对自己次小的有序临时节点状态
6.如果监视的次小节点状态发生变化,则跳转到步骤3,继续后续操作,直到退出锁竞争。


高可用

 通过投票选举leader


存放键值对

Zookeeper是以树状结果存放键值对的。

zookeeper的4种节点类型:

1、持久节点:节点创建后,会一直存在,不会因客户端会话失效而删除;

PERSISTENT (0, false, false), 

2、 持久顺序节点:基本特性与持久节点一致,创建节点的过程中,zookeeper会在其名字后自动追加一个单调增长的数字后缀,作为新的节点名; 

PERSISTENT_SEQUENTIAL (2, false, true), 

3、临时节点:客户端会话失效或连接关闭后,该节点会被自动删除,且不能再临时节点下面创建子节点,否则报如下错:org.apache.zookeeper.KeeperException$NoChildrenForEphemeralsException;

EPHEMERAL (1, true, false),

4、临时顺序节点:基本特性与临时节点一致,创建节点的过程中,zookeeper会在其名字后自动追加一个单调增长的数字后缀,作为新的节点名; 

EPHEMERAL_SEQUENTIAL (3, true, true);


 每个znode由3部分组成:

stat. 此为状态信息, 描述该znode的版本, 权限等信息.

data. 与该znode关联的数据.

children. 该znode下的子节点.


znode节点的状态信息

czxid. 节点创建时的zxid.

mzxid. 节点最新一次更新发生时的zxid.
ctime. 节点创建时的时间戳.
mtime. 节点最新一次更新发生时的时间戳.
dataVersion. 节点数据的更新次数.
cversion.        其子节点的更新次数.
aclVersion. 节点ACL(授权信息)的更新次数.
ephemeralOwner. 如果该节点为ephemeral节点, ephemeralOwner值表示与该节点绑定的session id,
如果该节点不是ephemeral节点, ephemeralOwner值为0.
dataLength. 节点数据的字节数.
numChildren. 子节点个数.

Data
zookeeper默认对每个结点的最大数据量有一个上限是1M,如果你要设置的配置数据大于这个上限将无法写法,

增加-Djute.maxbuffer=10240000参数 


持久化

 所有的操作都是存放在事务日志中的,可以用于数据恢复。

 快照是ZK的data tree的一份拷贝。每一个server每隔一段时间会序列化data tree的所有数据并写入一个文件。

二、安装配置

下载 http://zookeeper.apache.org/

配置

cat /opt/oracle/zookeeper/conf/zoo.cfg

dataDir=/opt/oracle/data/zookeeper/data

dataLogDir=/opt/oracle/data/zookeeper/datalog

clientPort=2181

initLimit=10

syncLimit=5

server.1=zookeeper01:2888:3888

server.2=zookeeper02:2888:3888

server.3=zookeeper03:2888:3888


参数名


说明


clientPort 客户端连接server的端口,即对外服务端口,一般设置为2181吧。

dataDir 存储快照文件snapshot的目录。默认情况下,事务日志也会存储在这里。建议同时配置参数dataLogDir, 事务日志的写性能直接影响zk性能。

tickTime ZK中的一个时间单元。ZK中所有时间都是以这个时间单元为基础,进行整数倍配置的。例如,session的最小超时时间是2*tickTime。

dataLogDir 事务日志输出目录。尽量给事务日志的输出配置单独的磁盘或是挂载点,这将极大的提升ZK性能。

(No Java system property)

globalOutstandingLimit 最大请求堆积数。默认是1000。ZK运行的时候, 尽管server已经没有空闲来处理更多的客户端请求了,但是还是允许客户端将请求提交到服务器上来,以提高吞吐性能。当然,为了防止Server内存溢出,这个请求堆积数还是需要限制下的。

(Java system property:zookeeper.globalOutstandingLimit.)

preAllocSize 预先开辟磁盘空间,用于后续写入事务日志。默认是64M,每个事务日志大小就是64M。如果ZK的快照频率较大的话,建议适当减小这个参数。(Java system property:zookeeper.preAllocSize)

snapCount 每进行snapCount次事务日志输出后,触发一次快照(snapshot), 此时,ZK会生成一个snapshot.*文件,同时创建一个新的事务日志文件log.*。默认是100000.(真正的代码实现中,会进行一定的随机数处理,以避免所有服务器在同一时间进行快照而影响性能)(Java system property:zookeeper.snapCount)

traceFile 用于记录所有请求的log,一般调试过程中可以使用,但是生产环境不建议使用,会严重影响性能。(Java system property:?requestTraceFile)

maxClientCnxns 单个客户端与单台服务器之间的连接数的限制,是ip级别的,默认是60,如果设置为0,那么表明不作任何限制。请注意这个限制的使用范围,仅仅是单台客户端机器与单台ZK服务器之间的连接数限制,不是针对指定客户端IP,也不是ZK集群的连接数限制,也不是单台ZK对所有客户端的连接数限制。指定客户端IP的限制策略,这里有一个patch,可以尝试一下:http://rdc.taobao.com/team/jm/archives/1334(No Java system property)

clientPortAddress 对于多网卡的机器,可以为每个IP指定不同的监听端口。默认情况是所有IP都监听clientPort指定的端口。New in 3.3.0

minSessionTimeoutmaxSessionTimeout Session超时时间限制,如果客户端设置的超时时间不在这个范围,那么会被强制设置为最大或最小时间。默认的Session超时时间是在2 * tickTime ~ 20 * tickTime这个范围 New in 3.3.0

fsync.warningthresholdms 事务日志输出时,如果调用fsync方法超过指定的超时时间,那么会在日志中输出警告信息。默认是1000ms。(Java system property:fsync.warningthresholdms)New in 3.3.4

autopurge.purgeInterval 在上文中已经提到,3.4.0及之后版本,ZK提供了自动清理事务日志和快照文件的功能,这个参数指定了清理频率,单位是小时,需要配置一个1或更大的整数,默认是0,表示不开启自动清理功能。(No Java system property) New in 3.4.0

autopurge.snapRetainCount 这个参数和上面的参数搭配使用,这个参数指定了需要保留的文件数目。默认是保留3个。(No Java system property)New in 3.4.0

electionAlg 在之前的版本中, 这个参数配置是允许我们选择leader选举算法,但是由于在以后的版本中,只会留下一种“TCP-based version of fast leader election”算法,所以这个参数目前看来没有用了,这里也不详细展开说了。(No Java system property)

initLimit Follower在启动过程中,会从Leader同步所有最新数据,然后确定自己能够对外服务的起始状态。Leader允许F在initLimit时间内完成这个工作。通常情况下,我们不用太在意这个参数的设置。如果ZK集群的数据量确实很大了,F在启动的时候,从Leader上同步数据的时间也会相应变长,因此在这种情况下,有必要适当调大这个参数了。(No Java system property)

syncLimit 在运行过程中,Leader负责与ZK集群中所有机器进行通信,例如通过一些心跳检测机制,来检测机器的存活状态。如果L发出心跳包在syncLimit之后,还没有从F那里收到响应,那么就认为这个F已经不在线了。注意:不要把这个参数设置得过大,否则可能会掩盖一些问题。(No Java system property)

leaderServes 默认情况下,Leader是会接受客户端连接,并提供正常的读写服务。但是,如果你想让Leader专注于集群中机器的协调,那么可以将这个参数设置为no,这样一来,会大大提高写操作的性能。(Java system property: zookeeper.leaderServes)。

server.x=[hostname]:nnnnn[:nnnnn] 这里的x是一个数字,与myid文件中的id是一致的。右边可以配置两个端口,第一个端口用于F和L之间的数据同步和其它通信,第二个端口用于Leader选举过程中投票通信。

(No Java system property)

group.x=nnnnn[:nnnnn]weight.x=nnnnn 对机器分组和权重设置,可以 参见这里(No Java system property)

cnxTimeout Leader选举过程中,打开一次连接的超时时间,默认是5s。(Java system property: zookeeper.cnxTimeout)

zookeeper.DigestAuthenticationProvider

.superDigest ZK权限设置相关,具体参见《使用super身份对有权限的节点进行操作》 和 《ZooKeeper权限控制》

skipACL 对所有客户端请求都不作ACL检查。如果之前节点上设置有权限限制,一旦服务器上打开这个开头,那么也将失效。(Java system property:zookeeper.skipACL)

forceSync 这个参数确定了是否需要在事务日志提交的时候调用FileChannel.force来保证数据完全同步到磁盘。(Java system property:zookeeper.forceSync)

jute.maxbuffer 每个节点最大数据量,是默认是1M。这个限制必须在server和client端都进行设置才会生效。(Java system property:jute.maxbuffer)



ZooKeeper服务命令:

在准备好相应的配置之后,可以直接通过zkServer.sh 这个脚本进行服务的相关操作

1. 启动ZK服务:       sh bin/zkServer.sh start

2. 查看ZK服务状态:   sh bin/zkServer.sh status

3. 停止ZK服务:       sh bin/zkServer.sh stop

4. 重启ZK服务:       sh bin/zkServer.sh restart




zk客户端命令

ZooKeeper命令行工具类似于Linux的shell环境,不过功能肯定不及shell啦,但是使用它我们可以简单的对ZooKeeper进行访问,数据创建,数据修改等操作. 

 使用 zkCli.sh -server 127.0.0.1:2181 连接到 ZooKeeper 服务,连接成功后,系统会输出 ZooKeeper 的相关环境以及配置信息。

命令行工具的一些简单操作如下:

1. 显示根目录下、文件: ls / 使用 ls 命令来查看当前 ZooKeeper 中所包含的内容

2. 显示根目录下、文件: ls2 / 查看当前节点数据并能看到更新次数等数据

3. 创建文件,并设置初始内容: create /zk "test" 创建一个新的 znode节点“ zk ”以及与它关联的字符串

4. 获取文件内容: get /zk 确认 znode 是否包含我们所创建的字符串

5. 修改文件内容: set /zk "zkbak" 对 zk 所关联的字符串进行设置

6. 删除文件: delete /zk 将刚才创建的 znode 删除 

7. 退出客户端: quit 

8. 帮助命令: help 




三、zookeeper 的 python api

zookeeper的python客户端安装

1.由于python客户端依赖c的客户端所以要先安装c版本的客户端



cd zookeeper-3.4.5/src/c  

./configure  

make   

make install  


2.测试c版本客户端

./cli_mt localhost:2181  

Watcher SESSION_EVENT state = CONNECTED_STATE  

Got a new session id: 0x23f9d77d3fe0001  


3、安装zkpython


 wget --no-check-certificate http://pypi.python.org/packages/source/z/zkpython/zkpython-0.4.tar.gz

 tar xf zkpython-0.4.tar.gz

 cd zkpython-0.4

python setup.py install



watch

watch的意思是监听感兴趣的事件. 在命令行中, 以下几个命令可以指定是否监听相应的事件.

ls命令. ls命令的第一个参数指定znode, 第二个参数如果为true, 则说明监听该znode的子节点的增减, 以及该znode本身的删除事件.

ls /test1 true

create /test1/01 123

get命令. get命令的第一个参数指定znode, 第二个参数如果为true, 则说明监听该znode的更新和删除事件.

get /test true

set /test test

stat命令. stat命令用于获取znode的状态信息. 第一个参数指定znode, 如果第二个参数为true, 则监听该node的更新和删除事件.




清理数据目录


快照是ZK的data tree的一份拷贝。每一个server每隔一段时间会序列化data tree的所有数据并写入一个文件



#!/bin/bash


#snapshot file dir

dataDir=/data/zookeeper/data/version-2

#tran log dir

dataLogDir=/data/zookeeper/datalog/version-2

#zk log dir

logDir=/data/zookeeper/log

#Leave 1 files

count=1

count=$[$count+1]

ls -t $dataLogDir/log.* | tail -n +$count | xargs rm -f

ls -t $dataDir/snapshot.* | tail -n +$count | xargs rm -f

ls -t $logDir/zookeeper.* | tail -n +$count | xargs rm -f



api






zkclient.py


#!/usr/bin/python

# -*- coding: UTF-8 -*-


import zookeeper, time, threading

from collections import namedtuple


zookeeper.set_debug_level(zookeeper.LOG_LEVEL_ERROR)

DEFAULT_TIMEOUT = 30000

VERBOSE = True


ZOO_OPEN_ACL_UNSAFE = {"perms":0x1f, "scheme":"world", "id" :"anyone"}


# Mapping of connection state values to human strings.

STATE_NAME_MAPPING = {

    zookeeper.ASSOCIATING_STATE: "associating",

    zookeeper.AUTH_FAILED_STATE: "auth-failed",

    zookeeper.CONNECTED_STATE: "connected",

    zookeeper.CONNECTING_STATE: "connecting",

    zookeeper.EXPIRED_SESSION_STATE: "expired",

}


# Mapping of event type to human string.

TYPE_NAME_MAPPING = {

    zookeeper.NOTWATCHING_EVENT: "not-watching",

    zookeeper.SESSION_EVENT: "session",

    zookeeper.CREATED_EVENT: "created",

    zookeeper.DELETED_EVENT: "deleted",

    zookeeper.CHANGED_EVENT: "changed",

    zookeeper.CHILD_EVENT: "child",

}


class ZKClientError(Exception):

    def __init__(self, value):

        self.value = value

    def __str__(self):

        return repr(self.value)


class ClientEvent(namedtuple("ClientEvent", 'type, connection_state, path')):

    """

    A client event is returned when a watch deferred fires. It denotes

    some event on the zookeeper client that the watch was requested on.

    """


    @property

    def type_name(self):

        return TYPE_NAME_MAPPING[self.type]


    @property

    def state_name(self):

        return STATE_NAME_MAPPING[self.connection_state]


    def __repr__(self):

        return  "<ClientEvent %s at %r state: %s>" % (

            self.type_name, self.path, self.state_name)



def watchmethod(func):

    def decorated(handle, atype, state, path):

        event = ClientEvent(atype, state, path)

        return func(event)

    return decorated


class ZKClient(object):

    def __init__(self, servers, timeout=DEFAULT_TIMEOUT):

        self.timeout = timeout

        self.connected = False

        self.conn_cv = threading.Condition( )

        self.handle = -1


        self.conn_cv.acquire()

        if VERBOSE: print("Connecting to %s" % (servers))

        start = time.time()

        self.handle = zookeeper.init(servers, self.connection_watcher, timeout)

        self.conn_cv.wait(timeout/1000)

        self.conn_cv.release()


        if not self.connected:

            raise ZKClientError("Unable to connect to %s" % (servers))


        if VERBOSE:

            print("Connected in %d ms, handle is %d"

                  % (int((time.time() - start) * 1000), self.handle))


    def connection_watcher(self, h, type, state, path):

        self.handle = h

        self.conn_cv.acquire()

        self.connected = True

        self.conn_cv.notifyAll()

        self.conn_cv.release()


    def close(self):

        return zookeeper.close(self.handle)


    def create(self, path, data="", flags=0, acl=[ZOO_OPEN_ACL_UNSAFE]):

        start = time.time()

        result = zookeeper.create(self.handle, path, data, acl, flags)

        #if VERBOSE:

        #    print("Node %s created in %d ms"

        #          % (path, int((time.time() - start) * 1000)))

        return result


    def delete(self, path, version=-1):

        start = time.time()

        result = zookeeper.delete(self.handle, path, version)

        if VERBOSE:

            print("Node %s deleted in %d ms"

                  % (path, int((time.time() - start) * 1000)))

        return result


    def get(self, path, watcher=None):

        return zookeeper.get(self.handle, path, watcher)


    def exists(self, path, watcher=None):

        return zookeeper.exists(self.handle, path, watcher)


    def set(self, path, data="", version=-1):

        return zookeeper.set(self.handle, path, data, version)


    def set2(self, path, data="", version=-1):

        return zookeeper.set2(self.handle, path, data, version)



    def get_children(self, path, watcher=None):

        return zookeeper.get_children(self.handle, path, watcher)


    def async(self, path = "/"):

        return zookeeper.async(self.handle, path)


    def acreate(self, path, callback, data="", flags=0, acl=[ZOO_OPEN_ACL_UNSAFE]):

        result = zookeeper.acreate(self.handle, path, data, acl, flags, callback)

        return result


    def adelete(self, path, callback, version=-1):

        return zookeeper.adelete(self.handle, path, version, callback)


    def aget(self, path, callback, watcher=None):

        return zookeeper.aget(self.handle, path, watcher, callback)


    def aexists(self, path, callback, watcher=None):

        return zookeeper.aexists(self.handle, path, watcher, callback)


    def aset(self, path, callback, data="", version=-1):

        return zookeeper.aset(self.handle, path, data, version, callback)


watch_count = 0


"""Callable watcher that counts the number of notifications"""

class CountingWatcher(object):

    def __init__(self):

        self.count = 0

        global watch_count

        self.id = watch_count

        watch_count += 1


    def waitForExpected(self, count, maxwait):

        """Wait up to maxwait for the specified count,

        return the count whether or not maxwait reached.


        Arguments:

        - `count`: expected count

        - `maxwait`: max milliseconds to wait

        """

        waited = 0

        while (waited < maxwait):

            if self.count >= count:

                return self.count

            time.sleep(1.0);

            waited += 1000

        return self.count


    def __call__(self, handle, typ, state, path):

        self.count += 1

        if VERBOSE:

            print("handle %d got watch for %s in watcher %d, count %d" %

                  (handle, path, self.id, self.count))


"""Callable watcher that counts the number of notifications

and verifies that the paths are sequential"""

class SequentialCountingWatcher(CountingWatcher):

    def __init__(self, child_path):

        CountingWatcher.__init__(self)

        self.child_path = child_path


    def __call__(self, handle, typ, state, path):

        if not self.child_path(self.count) == path:

            raise ZKClientError("handle %d invalid path order %s" % (handle, path))

        CountingWatcher.__call__(self, handle, typ, state, path)


class Callback(object):

    def __init__(self):

        self.cv = threading.Condition()

        self.callback_flag = False

        self.rc = -1


    def callback(self, handle, rc, handler):

        self.cv.acquire()

        self.callback_flag = True

        self.handle = handle

        self.rc = rc

        handler()

        self.cv.notify()

        self.cv.release()


    def waitForSuccess(self):

        while not self.callback_flag:

            self.cv.wait()

        self.cv.release()


        if not self.callback_flag == True:

            raise ZKClientError("asynchronous operation timed out on handle %d" %

                             (self.handle))

        if not self.rc == zookeeper.OK:

            raise ZKClientError(

                "asynchronous operation failed on handle %d with rc %d" %

                (self.handle, self.rc))



class GetCallback(Callback):

    def __init__(self):

        Callback.__init__(self)


    def __call__(self, handle, rc, value, stat):

        def handler():

            self.value = value

            self.stat = stat

        self.callback(handle, rc, handler)


class SetCallback(Callback):

    def __init__(self):

        Callback.__init__(self)


    def __call__(self, handle, rc, stat):

        def handler():

            self.stat = stat

        self.callback(handle, rc, handler)


class ExistsCallback(SetCallback):

    pass


class CreateCallback(Callback):

    def __init__(self):

        Callback.__init__(self)


    def __call__(self, handle, rc, path):

        def handler():

            self.path = path

        self.callback(handle, rc, handler)


class DeleteCallback(Callback):

    def __init__(self):

        Callback.__init__(self)


    def __call__(self, handle, rc):

        def handler():

            pass

        self.callback(handle, rc, handler)

if __name__ == '__main__':

    zk=ZKClient('10.10.79.185:2181,10.10.79.184:2181,10.10.79.183:2181')

    zk.create('/test1','123')

    zk.close


你可能感兴趣的:(zookeeper)