1,概念
Zookeeper是 Apache Hadoop项目下的一个子项目,是一个开源、分布式应用程序协调服务,为分布式应用提供一致性服务。
1)对比Eureka
zk是CP的:集群故障时不提供服务。
eureka是AP的。
CAP:
C(一致性);
A(可用性);
P(分区容错):分布式系统部分结点异常依然可以提供服务。
对于一个分布式系统,P是基本要求;只能在CA之间做权衡,同时通过其它办法提升P.
AP:
当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统的可用性。
此时不保证C。
CP:
当网络分区出现后,为了保证一致性,就必须拒绝请求,否则无法保证一致性。
此时不保证A。
2,使用场景
1)配置中心
- 存储配置信息;
例如集群中的机器配置、服务地址配置等。 - 统一管理配置信息,同时实现动态加载和更新。
2)集群管理
- 统一命名服务
Zookeeper可以用来实现命名服务,例如将集群中的机器名称和IP地址进行映射,或者将服务的唯一标识和实际地址进行映射。这样,客户端可以通过名称或标识来访问服务,而不需要知道服务的实际地址。 - 分布式同步
zk协调各个节点的同步,确保数据的一致性。 - 管理分布式集群
协调各个节点加入和退出; - 负载均衡
3)分布式锁
Zookeeper可以用来实现分布式锁,通过创建一个特殊的节点,各个节点可以竞争同一个锁,从而保证分布式系统中的一致性。
1>原理
- 在zk创建一个持久结点:ParentLock;
- client1想要获得锁时,在ParentLock下创建一个临时顺序结点:
- 如果这个临时顺序结点顺序第一,则获得锁。
执行完同步代码后释放锁:断开zk连接,临时结点会被删除。 - 如果这个临时顺序结点顺序非第一,则等待锁。
此时往前一个顺序结点注册Watcher,监听该结点是否存在。
若前一个结点被移除,则重新执行3、4去尝试获得锁。
2>优缺点
- 优点:预防死锁
由于临时结点的特性:客户端断开就会被删除,可以有效避免服务器宕机导致的死锁问题。 - 缺点:性能低、网络抖动导致并发问题。
由于zk锁需要动态创建、销毁结点来维持锁,比缓存服务性能差很多。
网络抖动导致客户端连接断开,从而导致锁丢失。==》zk有重试机制很少发生。
3>实现
zk的第三方库Curator客户端,封装了一个可重入锁服务。
Curator:
Java/JVM客户端库,用于zookeeper,一个分布式协调服务。
主要功能:
- 分布式锁;
- 服务注册与发现;
- 分布式队列:可以在多个进程或线程之间进行消息传递;
4)分布式队列
Zookeeper可以用来实现分布式队列,通过创建一个特殊的节点,各个节点可以加入或离开队列,同时队列中的节点可以按照一定的顺序进行排序。
5)服务注册和发现
zk服务发现配置复杂,主要是用于分布式协调和同步;而服务注册和发现的频率较高,性能差。
适合作为Dubbo的注册中心。
- 服务注册:服务在zk创建一个临时节点。
节点名称:服务名称+版本号;
节点数据:服务地址、端口、协议等。如果服务断开,临时节点从zk中删除。 - 服务发现与消费:watch服务节点
3,数据结构
树形目录结构:
1)ZNode(节点)
- 保存少量数据(1MB)、节点状态信息。
- 每个节点可以拥有子节点。
临时节点除外。
节点类型 | 说明 | 描述 |
---|---|---|
PERSISTENT | 持久节点 | 节点创建后,会一直存在,需要有删除操作进行删除; |
PERSISTENT_SEQUENTIAL | 持久顺序节点 | 持久结点的基础上,zk在节点名后自动追加数字后缀(单调增长,Integer.MAX); |
EPHEMERAL | 临时节点 | 客户端会话失效或连接关闭后,该节点会被自动删除; 临时节点不能创建子节点,否则报错:NoChildrenForEphemeralsException; |
EPHEMERAL_SEQUENTIAL | 临时顺序节点 | 临时节点,创建时有数字后缀; |
- 每个ZNode都有唯一路径标识。
- 不支持部分读写,仅支持一次性完整读写。
2)ACL(Access Control Lists,权限控制)
每个znode被创建时都会带有一个ACL列表,用于决定谁可以对它执行何种操作。
3)Watcher
每个ZNode可以配置Watcher:
- 监听节点中数据变化;
- 监听子目录变化。
可以设置观察的操作:exists,getChildren,getData;
可以触发观察的操作:create,delete,setData.
4,底层原理
1)角色
1>leader(领导者)
- 负责进行投票的发起和决议,更新系统状态。
- 为客户端提供读写服务。
- 无leader时zk会停止服务。
2>follower(跟随者)
- 用于接收客户端请求并响应客户端返回结果,在选举中参与投票;
- 为客户端提供读服务。
- 可被选举为leader
- follower节点越多,集群丢失数据的风险越低,但是,leader选举过程也要越长,写性能越慢。
3>observer(观察者)
- 接受客户端连接,将请求转发给leader;
- 不参与投票,只同步leader状态;不能成为leader。
- 扩展了系统,提高读取速度。
不影响写性能的情况下,扩展zookeeper集群,提供读的服务
4>client(客户端)
请求发起方。
2)CP
ZK是CP分布式系统。
ZK的主要职责:保证数据(配置数据、状态数据)在多个服务直接的同步一致性。
如果ZK结点失联(节点断开、网络分割故障)会直接剔除管理范围,即使这些节点正常,到达这些节点的服务请求都会被丢掉。
3)Leader选举
sid(Server id):服务器ID。用来唯一标识一台ZooKeeper集群中的机器,每台机器不能重复,和myid一致。sid越大,选举权重越大。
Zxid:事务ID。用来标识一次服务器状态的变更,值越大说明数据越新,选举中权重越大。
Epoch:逻辑时钟(投票次数)。同一轮投票Epoch相同,每轮投完Epoch增加。
Server状态:选举状态。
LOOKING:竞选状态。
FOLLOWING:随从状态;同步leader状态,参与投票。
OBSERVING:观察状态;同步leader状态,不参与投票。
LEADING:领导者状态。
1>场景
- 服务器初始化启动;
- 服务器运行期间leader故障;
非Observer服务器都会将自己的服务器状态变更为looing,开始进入leader选举。
2>选举原则
zookeeper默认使用快速选举:
- 向集群中的其他zookeeper建立连接,并且只有myid比对方大的连接才会被接受(也就是每2台只会有1个连接,避免连接浪费)
- 每台zookeeper默认先投自己,然后向集群广播自己的选票
- 收到对方的选票时,按权重由高到低依次比较:epoch(选举轮数)、zxid(事务id)、myid(服务器id),较大者胜出,改票并广播
- 如果收到的选票中有某个节点超过集群半数(>=n/2+1),则胜出当选为leader,其他节点为follower
4)脑裂
分布式系统由于故障被分为多个独立的子系统;这些子系统互相无法通信,独立运行,同时认为自己是整个系统的主节点;
脑裂导致整个系统失去一致性和可用性。
1>场景
zk靠过半机制、自增epoch机制不会发生脑裂。
- 网络故障
部分节点无法与其它节点通信。
预防:法定人数/多数机制(Quorum):半数投票才能选举leader。这样局部网络就不会产生新的leader,直接停止服务。==》zk对外提供服务的最小结点数量:m = n / 2 + 1(n为集群数量)。小于半数结点,集群将不可用。 - 主节点宕机
其它节点重新选取出了leader,但是之前的leader又恢复了。
预防:每次投票epoch都会自增,如果旧的leader复活,由于epoch不同,Follower会拒绝所有旧Leader发来的请求。
2>集群故障后自动恢复机制
- 识别集群分裂
某些结点失联(心跳包),识别到集群分裂问题; - 选举新的leader
- 数据同步
选举结束后,zk使用”原子广播“机制同步所有结点数据。 - 恢复正常状态
集群恢复正常状态。
3>手动恢复
- 寻找最新结点,其它结点数据可删除,后期会自动恢复。
5,常用命令
安装目录/usr/hdp/2.3.0.0-2557/zookeeper/bin下:
1)服务端常用命令
命令 | 说明 |
---|---|
./zkServer.sh start | 启动ZooKeeper服务 |
./zkServer.sh status | 查看ZooKeeper服务状态 |
./zkServer.sh stop | 停止ZooKeeper服务 |
./zkServer.sh restart | 重启ZooKeeper服务 |
2)客户端常用命令
说明 | 命令 | 备注 |
---|---|---|
连接ZooKeeper服务端 | ./zkCli.sh -server ip:port | |
断开连接 | quit | |
查看命令帮助 | help | |
显示指定目录下节点 | ls 目录 | |
创建节点 | create /节点path value | |
获取节点值 | get /节点path | |
设置节点 | set /节点path value | |
删除单个节点 | delete /节点path | |
删除带有子节点的节点 | deleteall/节点path | |
创建临时节点 | create -e /节点path value | |
创建顺序节点 | create -s /节点path value | |
查询节点详细信息 | get /节点path | czxid:节点被创建的事务ID ctime:创建时间 mzxid:最后一次被更新的事务ID mtime:修改时间 pzxid:子节点列表最后一次被更新的事务ID cversion:子节点的版本号 dataversion:数据版本号 aclversion:权限版本号 dataLength:节点存储的数据长度 numChildren:当前节点的子节点个数 |
查看指定结点权限 | getAcl path | |
给已有节点赋予权限 | setAcl path acl |
6,zk扩容
- 集群数量应该为奇数。
原因:n/2+1投票才能保证zk集群的一致性。
如果集群有5个节点,3>5\2,最多允许2个节点不可用。如果集群有6个节点,4>6\2,最多允许2个节点不可用。也就是说当集群节点数n为偶数时,其可用性与n-1是一样的,那我们何必多浪费一台机器呢? - 扩容后机器重启顺序:按照myid从小到大启动,最后启动leader节点。
zk只允许myid大的节点连接到myid小的节点。 - 数据目录:/home/zookeeper/data,备份:
cp -r data data.bak
;安装目录/home/zookeeper/software,目录不变,configure文件可以统一管理。 - 登录zookeeper 用户进行扩容。
- 集群扩容之后,替换所有需要用到zk的地方,将旧的所有server地址替换为所有server地址。
1)单机扩容
序号 | 节点名称 | myid | 说明 |
---|---|---|---|
1 | {OLD_SERVER} | 1 | 旧的节点 |
2 | {NEW_SERVER1} | 2 | |
3 | {NEW_SERVER2} | 3 |
- 将{OLD_SERVER} 的目录/home/zookeeper/software下的zookeeper目录打tgz包;在新机器的同目录下解压安装包
- 集群配置:
新机器中,vi /home/zookeeper/software/zookeeper/conf/zoo.cfg
,修改server配置项:
删除server.x=:xxxxxxxx:xxxx:xxxx
;文件后面追加:
#其中,1 2 3表示zk的id;后续配置会用到
server.1={ODL_SERVER}:2688:3788
server.2={NEW_SERVER1}:2688:3788
server.3={NEW_SERVER2}:2688:3788
调整dataDir配置为/home/zookeeper/data
- 数据目录
所有节点进入/home/zookeeper/data目录,echo "2" > myid
,其中2为上文提到的zk的id。 - 将新机器的zoo.cfg文件copy到原机器,保持配置文件一致。
- 重启zk,此时会有短暂的服务停止,关联的kafka等也会停止服务。所有结点启动成功后服务恢复正常。
##重启旧机器(一定先重启旧机器,保持集群的稳定状态)
cd /home/zookeeper/software/zookeeper/bin
./zkServer.sh restart
##启动新机器
cd /home/zookeeper/software/zookeeper/bin
./zkServer.sh start
- 扩容后集群校验
旧机器:./zkServer.sh status
可以看到有1个leader,2个follower。
2)集群扩容
假设3台机器扩容到5台:
- 新机器上解压老机器的zookeeper文件;
- 将老机器的zoo.cfg文件复制到新机器;并在新机器的zoo.cfg文件后追加:(先不要动老机器)
#其中,4 5表示zk的id;后续配置会用到
server.4={ODL_SERVER}:2688:3788
server.5={NEW_SERVER1}:2688:3788
- 在新机器的data目录,新建myid文件并写入zk的id。参加单机集群扩容。
- 将新机器的zoo.cfg文件copy到原机器,保持配置文件一致。
- 集群启动(zk会停止服务,也可能丢数据)
先停止follower状态的zk节点:zkServer.sh stop
然后停止其它zk结点;(这样就不会产生新的leader)
先启动老结点的zk:zkServer.sh start
;确保一台机器启动成功后,再重启下一台机器。
再启动新节点的zk服务 - 集群状态检测
zkServer.sh status
其中有一个leader,其它为follower状态。
3)增加observer节点
3个节点的kafka增加2个observer节点:
- 每个结点zoo.cfg追加:
server.4=hdp25.bigdata.zll.qianxin-inc.cn:4777:4666:observer
server.5=hdp25.bigdata.zll.qianxin-inc.cn:5777:5666:observer
对于新增节点,还要加上参数:peerType=observer
- 启动zk
关闭:先关闭follow再关闭leader;
启动:先重启follower,再重启leader,最后重启新增的observer - 集群检测
zkServer.sh status
server1、2、3为leader和follower,server4、5为observer