随机名言

Zookeeper



学习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 监听器原理

  1. 首先要有一个main()线程
  2. 在main线程中创建Zookeeper客户端,内有两个线程,分别负责网络连接(Connect)和监听(Listener)
  3. Connect将需要监听的事件发给Zookeeper
  4. Zookeeper进行监听事件的注册
  5. 发生监听的事件,Zookeeper将消息发送给Listener
  6. 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的死循环
解决思路:只有一个提案者


参考:

JavaGuide


posted @ 2020-10-18 22:20  Howlet  阅读(177)  评论(0编辑  收藏  举报

Copyright © By Howl