Permalink: 2014-12-26 17:17:23 by ning in misc

1   概况

  • 定位为: Distributed Coordination Service

  • 相当于一个replset, 在每个机器上都保存全量数据

  • client 只需要连上任何一个节点就可以读写

  • 单机对数据的处理方式像redis(数据在内存, 持久化在磁盘)
    • data in mem => 性能 不错.
    • transaction logs and snapshots in a persistent store. => 数据持久化

CAP中, zk是为C和A设计的.

  • 可用性高: 只要集群中大多数server 存活, 则zk集群是ok的

  • 一致性保证(具有事务的特点):
    • A: Atomicity - Updates either succeed or fail. No partial results.
    • C: Timeliness(最终一致性) - The clients view of the system is guaranteed to be up-to-date within a certain time bound.
    • I: Sequential Consistency - Updates from a client will be applied in the order that they were sent.
    • D: Reliability - Once an update has been applied, it will persist from that time forward until a client overwrites the update.
    • Single System Image - A client will see the same view of the service regardless of the server that it connects to.
  • 应用广泛: Hbase, Kafka, Spark, Storm

  • 从 2012年11月发布3.4.5之后, 就很少更新了.

1.1   数据模型

zk的名字空间是一棵树, 类似文件系统的file/dir:

/
├── apps
│   ├── message
│   │   ├── server0
│   │   ├── server1
│   │   └── server2
│   └── userinfo
└── conf

1.1.1   ZNode

  • ZNode 属性包含:
    • data < 1M (相当于value)
    • acl
    • timestamp
    • version
  • znode 有三种
    • Persistent Node: 永久有效存储, 除非显示删除

    • Ephemeral Nodes: 临时节点, 只有client Session有效期间, 节点才存在
      • 不能有Children
    • Sequence Nodes: 创建节点时, zk会在节点路劲末尾添加递增序号.
      • 可以用来实现分布式锁, 分布式queue.

create node时, CreateMode可以指定类型:

PERSISTENT
PERSISTENT_SEQUENTIAL
EPHEMERAL
EPHEMERAL_SEQUENTIAL

1.2   API

create
delete
exists
getChildren
getData
setData
getACL
setACL
sync: waits for data to be propagated

支持 CAS, 基于ZNode的版本号.

1.3   Session

  • ZK集群会为每一个client连接记录一个Session, 类似WebServer的session, 有过期机制,
  • 如果clinet连接断掉, 过期前重新连上的话, Session会继续有效.
  • 如果zk-server在Lease时间内未收到Client的任何消息, server认为session过期 (SESSION_EXPIRE)
  • 此时集群Leader发起SESSION_EXPIRE, 此时这个Session相关的信息都会被清除(Session本身, 它创建的临时节点, 添加的Watch)
  • Session expiration 是由ZooKeeper集群负责的, 而不是由client.

1.4   Watch

可以watch一个znode, 关注如下事件:

  • Node Children Changed
  • Node Create
  • Node Data Changed
  • Node Deleted

watch 的几个特点:

  • Watch 是一次性的(One Time Trigger)
    • 可能miss.
    • Client 需要解决得到一个event和发起下一个watch之间的时间差问题 TODO: how
  • Watch 保证有序.

  • watch 保存在Server端Session 中, 一个连接断掉重连后, 相关watch还在

  • 最好不要太多个client watch同一个znode, 容易产生惊群

  • A client will see a watch event for a znode it is watching before seeing the new data that corresponds to that znode.

A successful create() will trigger a data watch for the znode being created and a child watch for the parent znode. A successful delete() will trigger both a data watch and a child watch (since there can be no more children) for a znode being deleted as well as a child watch for the parent znode.

1.5   ACL

支持4种身份:

world:      anyone
auth:       anyone(登陆)
digest:     username:password
ip:         client host IP
  • 格式: (ip:19.22.0.0/16, READ)
  • ACL是设置在ZNode上,不包含Children
  • ACL不常用.

1.6   性能

1k data, 3server, 写大约3w/s, 读大约5w/s*3 (每台5w)

  • 节点越多, 读性能越好, 写性能越差(要同步)

1.7   典型应用场景

常见:

  • 全局配置服务
  • 分布式锁
  • 用于选主

少见:

  • 队列
  • Barriers
  • 两段提交

2   原理

Paxos: 每个写请求, Leader节点会要求所有Follower投票, 投票超过n/2+1通过时 才成功, 保证一致性, 但是不是强一致.

  • 三种角色:
    • Leader: 接受读+写请求.

    • Follower 接受读, 写请求转发到Leader

    • Observer: 接受读, 写请求转发到Leader,
      • 和Follower的区别是: 写操作会要求Follower投票, 但是不要求Observers投票, Observers只同步其他节点投票的结果.
  • read: 连上哪个server就在哪个server操作

  • write: 转发到Leader, 由Leader 发起投票

2.1   关于Observer

  • 为了提高读性能, 需要增加节点, 但是增加节点后写性能又降低, 悲剧
  • 这是因为 写需要一半节点投票同意,
  • 所以增加了一种节点类型: Observer, 不投票, 只接受最后的结果,

Observer表现和常规Follower一样:

  • 接受读写.
  • 读本地, 写转发到Leader

常见用法: 在单个机房内组成一个投票集群,外围的机房都会是一个observer集群和投票集群进行数据交互。

3   应用

3.1   选主

1. getdata(“/servers/leader”, true)
2. if successful follow the leader described in the data and exit
3. create(“/servers/leader”, hostname, EPHEMERAL)
4. if success, I'm lead and exit
5. goto step 1

3.3   分布式锁

所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。

3.4   全局时序

/distribute_lock 已经预先存在,客户端在它下面创建临时有序节点(这个可以通过节点的属性控制:CreateMode.EPHEMERAL_SEQUENTIAL来指定)。Zk的父节点(/distribute_lock)维持一份sequence,保证子节点创建的时序性,从而也形成了每个客户端的全局时序。

4   使用&配置

4.1   配置

单机模式:

tickTime=2000
dataDir=/tmp/zk
clientPort=2181

tickTime:这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。 dataDir:顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里。 clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。

集群模式增加如下配置:

initLimit=5     # 连接到Leader的超时(单位为tickTime)
syncLimit=2     # Leader 与 Follower 之间发消息 的超时 (单位为tickTime)
server.1=192.168.211.1:2888:3888    #
server.2=192.168.211.2:2888:3888    #

dataDir/myid文件: 对应server.1 的 1

4.2   试用

1. download

ning@ning-mac ~/Downloads/zookeeper-3.4.6$ cp conf/zoo_sample.cfg conf/zoo.cfg

ning@ning-mac ~/Downloads/zookeeper-3.4.6$ ./bin/zkServer.sh start
JMX enabled by default
Using config: /Users/ning/Downloads/zookeeper-3.4.6/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED

[zk: 127.0.0.1:2181(CONNECTED) 0] ning@ning-mac ~/Downloads/zookeeper-3.4.6$ bin/zkCli.sh
Connecting to localhost:2181
[zk: localhost:2181(CONNECTED) 1] help
ZooKeeper -server host:port cmd args
    connect host:port
    get path [watch]
    ls path [watch]
    set path data [version]
    rmr path
    delquota [-n|-b] path
    quit
    printwatches on|off
    create [-s] [-e] path data acl
    stat path [watch]
    close
    ls2 path [watch]
    history
    listquota path
    setAcl path acl
    getAcl path
    sync path
    redo cmdno
    addauth scheme auth
    delete path [version]
    setquota -n|-b val path

[zk: localhost:2181(CONNECTED) 2] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 3] create /zk_test my_data
Created /zk_test
[zk: localhost:2181(CONNECTED) 4] ls /
[zookeeper, zk_test]
[zk: localhost:2181(CONNECTED) 5] ls /z

zookeeper   zk_test
[zk: localhost:2181(CONNECTED) 5] ls /zk_test
[]
[zk: localhost:2181(CONNECTED) 6] get /zk_test
my_data
cZxid = 0x6
ctime = Wed Apr 01 16:05:47 CST 2015
mZxid = 0x6
mtime = Wed Apr 01 16:05:47 CST 2015
pZxid = 0x6
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0

4.3   监控命令

dump
envi
kill
reqs
ruok        are you ok
stat
srst        重置stat.

4.4   相关工具

  • pip install zk_shell
  • 可以加一层proxy解决连接数问题(对读有效).

5   小结&注意

  • 适合读多写少的业务

  • 读可以通过增加实例水平扩展
    • 节点越多, 读能力越高, 但是写能力越差.
  • zk 是比redis复杂的系统, server处理的对象除了存储, 还有session, 连接, 投票.

注意:

  • zk并不是强一致, 而是最终一致 => 有主从延迟.

  • 可用性并不是100%:
    • 当集群发生重新选举期间, 服务不可用, (重新选举需要同步数据, 所以时间比较长)
  • 正确使用watch需要一定经验.

Comments