Zookeeper 分布式应用的分布式协调服务

Zookeeper简介

ZooKeeper是一种为分布式应用所设计的高可用、高性能且一致的开源协调服务, 一些基本应用场景:分布式锁服务、配置维护、组服务、分布式消息队列、分布式通知/协调等。

Zookeeper客户端

客户端连接一个单独的ZooKeeper服务端。客户端维护了一个TCP 的连接, 通过它来发送请求、获取响应、获取watch事件以及发送心跳。 如果与服务端的连接断了,客户端将会连到其他的服务端。

以golang客户端go-zookeeper为例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
func Connect(servers []string, sessionTimeout time.Duration, options ...connOption) (*Conn, <-chan Event, error) {
    /*...*/
    go func() {
        conn.loop(ctx) //循环获取服务器地址建立连接,并发送心跳和接收消息
        conn.flushRequests(ErrClosing)
        conn.invalidateWatches(ErrClosing)
        close(conn.eventChan)
    }()
    return conn, ec, nil
}

Zookeeper数据结构

ZooKeeper命名空间中的Znode,兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL、时间戳等数据结构, 又像目录一样可以作为路径标识的一部分。图中的每个节点称为一个Znode。 Local Picture

ZooKeeper数据结构特点:

  1. znode标识:每个子目录项都是一个znode,znode被它所在的路径唯一标识,如 Server1 这个 znode 的标识为 /NameService/Server1
  2. znode可以有子节点目录: 每个 znode 可以存储数据,EPHEMERAL 类型的目录节点不能有子节点目录
  3. znode有版本:每个 znode 中存储的数据可以有多个版本,也就是一个访问路径中可以存储多份数据
  4. znode 可以是临时节点: 一旦创建这个 znode 的客户端与服务器失去联系,这个 znode 也将自动删除
  5. znode 的目录名可以自动编号: 如 App1 已经存在,再创建的话,将会自动命名为 App2
  6. znode 可以被监控: 这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端

每个Znode由3部分组成:

  1. stat:状态信息, 描述该Znode的版本, 权限等信息
  2. data:Znode关联的数据
  3. children:Znode下的子节点

watch事件类型

1
2
3
4
5
6
7
8
9
const (
    EventNodeCreated         EventType = 1 // 创建节点成功通知
    EventNodeDeleted         EventType = 2 // 节点移除通知
    EventNodeDataChanged     EventType = 3 // 当前节点数据变化通知
    EventNodeChildrenChanged EventType = 4 // 子节点列表变化通知

    EventSession     EventType = -1
    EventNotWatching EventType = -2
)

watch事件状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const (
    StateUnknown           State = -1 // 未知状态
    StateDisconnected      State = 0  // 断开连接
    StateConnecting        State = 1  // 连接中
    StateAuthFailed        State = 4  // 权限异常
    StateConnectedReadOnly State = 5  // 当前连接仅支持读操作
    StateSaslAuthenticated State = 6
    StateExpired           State = -112 // 会话超时

    StateConnected  = State(100) // 已经连接
    StateHasSession = State(101)
)

创建节点时flags参数类型:

1
2
3
4
5
6
const (
    // 0 持久节点
    FlagEphemeral = 1 // 临时节点
    FlagSequence  = 2 // 顺序节点
    FlagTTL       = 4 // 
)

创建节点时控制权限参数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Constants for ACL permissions
const (
    PermRead = 1 << iota // 可以读取节点数据及显示子节点列表
    PermWrite            // 可以设置节点数据
    PermCreate           // 可以创建子节点
    PermDelete           // 可以删除子节点(仅下一级节点)
    PermAdmin            // 可以设置节点访问控制列表权限
    PermAll = 0x1f       // 所有权限
)

// world模式,anyone是唯一值,表示所有人
func WorldACL(perms int32) []ACL {
    return []ACL{{perms, "world", "anyone"}}
}

// auth模式,表示所有通过身份验证的用户
func AuthACL(perms int32) []ACL {
    return []ACL{{perms, "auth", ""}}
}

Znode状态信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
type Stat struct {
    Czxid          int64 // 创建节点的事物ID
    Mzxid          int64 // 修改节点的事务ID
    Ctime          int64 // 创建时间(milliseconds)
    Mtime          int64 // 最后修改时间(milliseconds)
    Version        int32 // 数据版本变更次数
    Cversion       int32 // 子节点进行的更改次数(不包括子节点)
    Aversion       int32 // 权限版本变更次数
    EphemeralOwner int64 // 临时节点会话属性ID
    DataLength     int32 // 数据长度
    NumChildren    int32 // 子节点数(不包括子子节点)
    Pzxid          int64 // 子节点变更的事务ID
}

参考