zookeeper


分布式架构及一致性
ZooKeeper安装配置及使用
ZooKeeper基础核心:数据模型、Node、Watch、版本、ACL
ZooKeeper基础核心:服务器角色、会话与客户端、数据与存储
Zookeeper编程手册:ZkClient、Curator
ZooKeeper硬核深入:Paxos算法及ZAB协议
ZooKeeper源码深入:Leader选举解析、序列化与通信协议
ZooKeeper应用实战:分布式锁、Master选举、配置中心、服务注册

分布式数据一致性解决方案

分布式协调服务,保证系统的各种数据(配置/状态)在分布式环境中的一致性

硬件、软件 分布式部署在网络上的不同计算机上,消息传递进行通讯和协调

分布式中的问题

  1. 故障总会发生:宕机/网络
  2. 时钟同步:分布式ID生成

CAP

C:Consistency 一致性

A:Availability 可用性

P:Partition Tolerance 分区一致性

BASE理论 —— 解决数据一致性的问题

BA:basic 基本可用 ——> 服务降级

S:soft 软状态

E: 最终一致性

ACID

Atomicity:原子性

Consistency:一致性

Isolution:事务隔离性

Durability:持久性

2PC

3PC

三、Zookeeper安装配置

环境需求:JDK 1.7以上

集群模式:单机模式、集群模式,伪分布式模式

nnnn同步端口,mmmm选举端口

下载解压

动态变更配置

zookeeper 3.5 开始支持

查找主节点

命令:[root@dn3 zookeeper]# bin/zkServer.sh status conf/zoo.cfg

1
2
3
4
[root@dn3 ~]# service zookeeper status
ZooKeeper JMX enabled by default
Using config: /data/soft/new/zookeeper-3.4.14/bin/../conf/zoo.cfg
Mode: leader

四、核心基础

数据如何同步

Leader:读、写、选举

Follower:读、选举

Observer:读

客户端

[root@dn3 zookeeper]# bin/zkCli.sh

过半机制

3节点的集群至少有2台集群才能工作,少于两台则会罢工

它的判断机制是 节点数 >= n/2

4台过半,则是2台;所以偶数台则容易脑裂

脑裂

过半机制 + 奇数个节点服务器 解决脑裂问题

ZAB协议

ZK命令行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[zk: localhost:2181(CONNECTED) 1] help
ZooKeeper -server host:port cmd args
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port

create

1
2
3
4
5
6
7
8
9
10
create [-s] [-e] path data acl
-s: 节点是否有序
-e: 创建临时节点,临时节点只在session有效,断开就消失

[zk: localhost:2181(CONNECTED) 3] create -s -e /cz 001
Created /cz0000000000
[zk: localhost:2181(CONNECTED) 4] create -s -e /cz 002
Created /cz0000000001
[zk: localhost:2181(CONNECTED) 5] create -s -e /cz 003
Created /cz0000000002
1
2
3
4
5
6
7
8
9
10
11
12
13
[zk: localhost:2181(CONNECTED) 7] create -e /cz 005   
Created /cz
# 临时节点不能有子节点
[zk: localhost:2181(CONNECTED) 8] create -e /cz/001 005
Ephemerals cannot have children: /cz/001
# 持久节点不能级联创建,只能一个一个创建
[zk: localhost:2181(CONNECTED) 9] create /czq/001 005
Ephemerals cannot have children: /cz/001

[zk: localhost:2181(CONNECTED) 12] create /czq 1
Created /czq
[zk: localhost:2181(CONNECTED) 13] create /czq/001 123
Created /czq/001

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[zk: localhost:2181(CONNECTED) 9] get /czq/001
123
cZxid = 0x70000000e
ctime = Fri Mar 06 21:37:11 CST 2020
mZxid = 0x70000000e
mtime = Fri Mar 06 21:37:11 CST 2020
pZxid = 0x70000000e
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
[zk: localhost:2181(CONNECTED) 10] create -e /czq/002 456
Created /czq/002
[zk: localhost:2181(CONNECTED) 11] get /czq/002
456
cZxid = 0x700000011
ctime = Fri Mar 06 21:43:46 CST 2020
mZxid = 0x700000011
mtime = Fri Mar 06 21:43:46 CST 2020
pZxid = 0x700000011
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x3000000469d0002
dataLength = 3
numChildren = 0

两大神兽:Znode 和 Watcher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 在 session1 启用监听
[zk: localhost:2181(CONNECTED) 13] ls /czq watch
[001, 002]

# 在 session2 创建新节点
[zk: localhost:2181(CONNECTED) 1] create -e /czq/003 003
Created /czq/003
[zk: localhost:2181(CONNECTED) 2]

# 查看session1,可以看到,监听到了事件
[zk: localhost:2181(CONNECTED) 14]
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/czq

# 监听变更
[zk: localhost:2181(CONNECTED) 18] get /czq/003 watch
[zk: localhost:2181(CONNECTED) 19]
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/czq/003

官方API:注册一次watch只能监听一次事件

客户端 ZooInspector

ACL 访问控制等级

ACL(Access Control Level)

1
2
3
create [-s] [-e] path data acl
setAcl path acl
getAcl path
  • 权限模式 Schema
  • 授权对象 ID
  • 权限 Permission

示例

1
2
3
[zk: localhost:2181(CONNECTED) 3] getAcl /
'world,'anyone
: cdrwa

权限模式 Schema

  1. world 只有一个id, anyone world:anyone
  2. Authorware 通过了ZK验证的用户都有权限 username/password
  3. Digest username:password, Base64(SHA1(username:password))=username:password
  4. ip或ip段
  5. 超级管理员 Super:cdrwa

权限 Permission(cdrwa)

  • Create
  • Delete
  • Read
  • Write
  • Admin
1
2
3
4
5
6
7
8
9
10
11
12
13
[zk: localhost:2181(CONNECTED) 11] setAcl /czq/001  ip:192.168.56.105:cdrw
[zk: localhost:2181(CONNECTED) 12] getAcl /czq/001
Authentication is not valid : /czq/001

[zk: localhost:2181(CONNECTED) 18] addauth digest test:123456
[zk: localhost:2181(CONNECTED) 19] create -e /czq/001 001 digest:test:123456:cdrw
Created /czq/001

# 没有权限的可以delete,不能rmr
[zk: localhost:2181(CONNECTED) 15] delete /czq/001
[zk: localhost:2181(CONNECTED) 16] ls /czq
[]
[zk: localhost:2181(CONNECTED) 17]

会话与客户端

zk 的 server 和 client 之间是基于 netty 的长连接

tickTime

会话激活

会话分桶

数据和存储

内存数据、磁盘数据(日志、快照)

  • 内存数据 ==> 定期生成快照
  • 操作日志
  • 快照 + 操作日志 ==> 用于崩溃恢复

DataTree

ZKDatabase

log.xxxxxxxxxxx 当前事务日志的第一条记录zxid(64位,高32位,低32位)

64M,第一条默认是4K大小,超过以后 扩容到64M

解析日志:

五、zk客户端

1、zk原生API

zk如何做负载均衡?

在初始化zk对象的时候,将传入的zk服务器列表打散,形成一个环形队列

2、zkClient

3、Curator

API使用建造者模式,链式调用,非常友好

六、zk用途

1、配置中心

2、

3、分布式锁

七、深入了解

1、Leader选举过程

zk内部源码:Leader、Follower、Observer

2、Master选举

zk的应用场景

3、分布式锁

4、服务注册发现

为了解耦,实现服务的动态发布。动态感知服务上下线

provider,consumer

其他组件:zookeeper、consul、eureka

用到的服务:dubbo,

Paxos + ZAB

崩溃恢复和Leader选举

ZAB(Zookeeper Atomic Broadcast)

八、源码

leader选举

Election:选举接口

FastLeaderElection:选举实现类

ClientConxn:客户端交互 (Packet 包)

ServerCnxn:

QuorumPeer(extends ZooKeeperThread):集群节点

QuorumPeerMain:启动入口

ZKDatabase:zk内存数据库

FileTxnSnapLog:事务快照日志

启动入口:org.apache.zookeeper.server.quorum.QuorumPeerMain

org/apache/zookeeper/server/quorum/QuorumPeer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public synchronized void start() {
if (!getView().containsKey(myid)) {
throw new RuntimeException("My id " + myid + " not in the peer list");
}
loadDataBase();
startServerCnxnFactory();
try {
adminServer.start();
} catch (AdminServerException e) {
LOG.warn("Problem starting AdminServer", e);
System.out.println(e);
}
startLeaderElection();
super.start();
}

paxos实现

lookForLeader