学习Dubbo需要用到注册中心,前来学习
1. 概述
ZooKeeper本身是分布式的,是一个为分布式应用提供协调服务的一个Apache项目,常用于注册中心
底层基于观察者模式设计,主要负责存储和管理数据并且接收观察者的注册,数据更新时通知观察者
1.1 特点
- 可组成集群:一个Leader,多个Follower
- 集群中半数以上节点存活,Zookeeper集群才能正常服务
- 全局数据一致,集群每个数据内容都一致
- 更新请求顺序进行:来自同一个Client的更新请求按其发送顺序依次执行
- 数据更新原子性:集群集群都成功应用了某个事务
- 实时性:一定时间范围内能读取到最新的数据,也是ZK突出的特点
1.2 数据结构
ZK的数据模型和Linux的文件系统类似,整体看成一棵树,每个节点称为ZNode。每一个ZNode默认可存储1MB的数据,每个ZNode通过其路径唯一标识,其主要用于协调服务,而不是存储数据
1.2.1 节点类型分为:
- 持久(Persistent)型,客户端和服务器断开连接,创建的节点不删除
- 持久顺序型(Persistent_Sequential),在持久性基础上,名字中添加了序号
- 短暂型(Ephemeral),客户端和服务器断开连接,创建的节点自己删除,并且只能做叶子节点,不能创建子节点
- 短暂顺序型(Ephemeral_Sequential),同理
1.2.2 节点数据分为:
- stat :状态信息
- data :节点存放的数据的具体内容
[zk: 127.0.0.1:2181(CONNECTED) 0] get /dubbo
# data 存放的数据
192.168.XXX.XXX
# stat 格式化输出有下表内容
node 状态信息 | 解释 |
---|---|
cZxid | create ZXID,即该数据节点被创建时的事务 id |
ctime | create time,即该节点的创建时间 |
mZxid | modified ZXID,即该节点最终一次更新时的事务 id |
mtime | modified time,即该节点最后一次的更新时间 |
pZxid | 该节点的子节点列表最后一次修改时的事务 id,只有子节点列表变更才会更新 pZxid,子节点内容变更不会更新 |
cversion | 子节点版本号,当前节点的子节点每次变化时值增加 1 |
dataVersion | 数据节点内容版本号,节点创建时为 0,每更新一次节点内容(不管内容有无变化)该版本号的值增加 1 |
aclVersion | 节点的 ACL 版本号,表示该节点 ACL 信息变更次数 |
ephemeralOwner | 创建该临时节点的会话的 sessionId;如果当前节点为持久节点,则 ephemeralOwner=0 |
dataLength | 数据节点内容长度 |
numChildren | 当前节点的子节点个数 |
ACLZooKeeper 采用 ACL(AccessControlLists)策略来进行权限控制,类似于 UNIX 文件系统的权限控制
1.3 应用场景
- 统一配置管理:将多个系统共用的配置文件放入ZooKeeper里监听变化即可
- 统一命名管理:根据指定名字来获取资源或服务的地址
- 统一集群管理:集群中保证数据一致性
- 服务器节点动态上下线:
- 负载均衡:
- 分布式锁:数据更新的原子性使用version的乐观锁
- 数据发布/订阅:watcher监听机制
- 分布式协调/通知:采用了Watcher(事件监听器),即在特定节点上注册监听器,事件触发时通知客户端
1.4 安装
- 下载 安装包并解压
- 修改conf里面的配置,并重命名为
zoo.cfg
# 修改数据的存放地址
dataDir=../zkData
- 启动ZK(zkServer:2181)
- 启动ZK客户端(zkCli)
客户端操作需要添加依赖 Curator
1.5 身份认证
- world:默认方式,所有用户都可无条件访问
- auth:不使用任何 id,代表任何已认证的用户
- digest:用户名:密码认证方式: username:password
- ip:对指定 ip 进行限制
2. 基本命令与使用
命令比较简单,列出概要自己探索一下即可
# 创建节点
create [-e/-s] 节点 关联内容 create /howl 12346
# 更新节点内容
set 节点 关联内容 set /howl 11111
# 查看某路径的子节点
ls 节点路径 ls /howl
# 获取节点内容
get [watch监听] 节点 get /howl
# 删除节点
delete/rmr 节点 delete /howl
# 退出客户端
quit
3. 底层原理
3.1 选举机制
其为半数机制,即集群中半数以上存活才有效。虽然配置文件中没有指定Master和Slave,但Leader是通过内部的选举机制临时产生的,其流程如下:
假设5台服务器一个个依次启动:
- 服务器1启动:给自己投票,然后发投票信息。由于没有通信对象,服务器1处于Looking(选举状态)
- 服务器2启动:给自己投票,与之前启动的服务器1交换选举数据,服务器2编号大胜出,但投票数没有大于半数,所以两个服务器的状态依然是LOOKING
- 服务器3启动:给自己投票,与之前启动的服务器1,2交换选举数据,服务器3的编号最大所以服务器3胜出,此时投票数大于半数,所以服务器3成为Leader,服务器1,2成为Slave
- 服务器4启动:给自己投票,与之前启动的服务器1,2,3交换信息,虽然服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为Slave
- 服务器5启动:同理服务器5成为Slave
3.2 分布式部署
- 解压安装Zookeeper到各个服务器(hadoop可以同步各个服务器??)
- 配置每个服务器的编号(myid)
# 在 /zkData 目录下创建一个myid文件
touch myid
# 往里面添加编号,一个数字即可
1
- 配置zoo.cfg文件
#############Cluster###############
server.2 = 127.0.0.1:22:22
server.3 = 127.0.0.1:23:23
server.4 = 127.0.0.1:24:24
- 逐个启动
3.3 监听器原理
- 首先要有一个main()线程
- 在main线程中创建Zookeeper客户端,内有两个线程,分别负责网络连接(Connect)和监听(Listener)
- Connect将需要监听的事件发给Zookeeper
- Zookeeper进行监听事件的注册
- 发生监听的事件,Zookeeper将消息发送给Listener
- Listener线程内部调用process()方法
3.4 写数据流程
- Client向Zookeeper的follower写数据,那么follower会将请求转发给Leader
- Leader再将请求广播给每个follower,follower写成功后会通知Leader
- Leader收到半数以上的写成功通知后会认为写数据成功,然后通知最初的follower
- 最初的follower会进一步通知Client写数据成功
3.5 Session
客户端与ZooKeeper建立会话是用TCP长连接的,使用心跳检测来保持会话有效,这个连接可请求响应,以及接收监听事件。断开时长不超过sessionTimeout,那么重连后之前创建的会话有效
3.6 分布式锁
多个系统访问锁节点
,那么每个系统都会在锁节点下
创建一个带序号的临时节点
序号最小的临时节点获取到锁,执行完操作则删除自身的临时节点
其余临时节点监听序号比自己小1的节点
3.7 集群
最常见的集群就是主从复制了,采用Master选举方法。
Master负责提供写服务、而其他Slave负责异步同步数据,提供读服务
3.8 一致性问题CAP 和 算法
- 2PC(二阶段提交)
1. 协调者向参与者发送prepare,要参与者执行事务但不提交并记录undo,redo日志,执行后反馈给协调者
2. 当所有参与者准备好后,协调者发送commit请求,即参与者全提交、所有参与者没有准备则进行rollback
单点故障问题:协调者挂了
阻塞问题:协调者发送prepare请求阻塞挂了,参与者占有资源不释放
数据不一致:协调者发送了一部分commit就挂了
- 3PC(三阶段提交)
1. 向所有参与者发送CanCommit,查看是否都可以执行事务,是就进入预备状态
2. 向所有参与者发送PreCommit,参与者执行事务不提交写日志响应结果。若收到一阶段NO或超时,会群发中断请求
3. 在二阶段全部YES之后,群发DoCommit进行提交事务,并接收响应。若收到二阶段NO或超时,群发中断请求
解决了阻塞问题,有超时机制
- Paxos
Paxos算法是基于消息传递且具有高度容错特性的一致性算法,是目前公认的解决分布式一致性问题最有效的算法之一
1. 每个提案者提案时获取一个全局唯一性编号N,赋予提案
每个表决者接受某提案后,将编号存到本地,以后仅接受大于本地编号的提案,最后将最大编号反馈给提案者
2. 当提案者的提案超过半数表决者接受,那么此提案为真,群发此提案内容和编号
表决这收到为真的提案,比较自己本地最大编号,大于则接受,小于则反馈NO
有提案A被批准,提案B又批准,那么A编号小于B,重新提出编号+1的死循环
解决思路:只有一个提案者
参考: