1.Zookeeper介绍
- 配置文件
tickTime :2000 #发送心跳的时间间隔,单位毫秒
initLimit=10 #从leader服务器上同步数据的最大时间限制
syncLimit=5 #检测是否机器已经下线
dataDir=/tmp/zookeeper #存储快照文件,默认情况日志也会存储在这,可以配置dataLogDir
clientPort: =2181 #客户端连接服务端的接口
--------------------------------------------------
dataDir=/zookeeper/data_1
dataLogDir=/zookeeper/logs_1
server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
#server.x中的x和myid中的一致,第一个端口用户Leader和Learner之间的同步,第二个端口用于选举过程中的投票通信
- 启动集群
> zkServer.sh start {zookeeperUrl}/zookeeper-3.4.12/conf/zoo1.cfg
> zkServer.sh start {zookeeperUrl}/zookeeper-3.4.12/conf/zoo2.cfg
> zkServer.sh start {zookeeperUrl}/zookeeper-3.4.12/conf/zoo3.cfg
- 查看服务状态
> zkServer.sh status {zookeeperUrl}zookeeper-3.4.12/conf/zoo1.cfg
> zkServer.sh status {zookeeperUrl}zookeeper-3.4.12/conf/zoo2.cfg
> zkServer.sh status {zookeeperUrl}zookeeper-3.4.12/conf/zoo3.cfg
- 客户端连上服务器
> zkCli.sh -server localhost:2181
> zkCli.sh -server localhost:2182
> zkCli.sh -server localhost:2183
- 观察者节点
节点配置中加上:peerType=observer
server列表加上:server.4=localhost:2890:3890:observer
- 集群的角色术语
领导者(Leader) :负责进行投票的发起和决议,最终更新状态。
跟随者(Follower) : Follower用于接收客户请求并返回客户结果。参与Leader发起的投票。
观察者(observer) : Oberserver可以接收客户端连接,将写请求转发给leader节点。
但是Observer不参加投票过程,只是同步leader的状态。
学习者( Learner ) :和leader进行状态同步的server统称Learner,Follower和Observer都是Learner.
- Zookeeper中的CAP理论
Zookeeper至少满足了CP,牺牲了可用性,
如果挂掉的是Leader或Follower,都要重新进行选举,在选举过程中,集群是不可用的。
如果挂掉的是Observer,那么对于集群来说并没有影响,集群还是可以用的。
2.Zookeeper详细功能介绍
- 节点类型
- 持久节点:节点创建就一直存在,会进行持久化;
- 临时节点:和客户端的生命周期绑定,不会持久化;
- 持久顺序节点:有序
- 临时顺序节点:有序
- watch监听器
- 一个Watch事件,当被设置了Watch的数据发生了改变的时候,
- 服务器将这个改变发送给设置了Watch的客户端(监听器),来进行通知。
- ACL权限控制
ACL 权限控制,使用:scheme:id:perm 来标识
scheme:
- world:默认方式,相当于全部都能访问;
- auth:代表已经认证通过的用户(cli中可以通过addauth digest user:pwd 来添加当前上下文中的授权用户)
- digest:即用户名:密码这种方式认证,这也是业务系统中最常用的。
- ip:使用客户端的主机IP作为ACL ID 。
id:
- anyone、user:pwd、ip地址。
perm:
- CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、删、改、查、管理权限,这5种权限简写为crwda。
- 常用命令
创建持久节点 create /path data创建临时节点 create -e /path data
获取节点数据 get /path
设置节点数据 set /path /data
检查节点状态 stat /FirstNode
递归删除节点 rmr /FirstNode、
查看ACL getAcl /parent
设置ACL 设置setAcl /parent world:anyone:wa
增加ACL的设置 增加授权用户addauth digest zhangsan:12345 ,授予权限setAcl /parent auth:zhangsan:123456:rdwca
- 节点的状态
cZxid:创建znode的事务ID。
mZxid:最后修改znode的事务ID。
pZxid:最后修改添加或删除子节点的事务ID。
ctime:表示znode创建时间,时间戳。
mtime:表示znode最近修改时间,时间戳。
dataVersion:表示对该znode的数据所做的更改次数。
cversion:这表示对此znode的子节点进行的更改次数。
aclVersion:表示对此znode的ACL进行更改的次数。
ephemeralOwner:如果znode是ephemeral类型节 点,则这是znode所有者的session ID。如果znode不 是ephemeral节点,则该字段设置为零。
dataLength:这是znode数据字段的长度。
numChildren:这表示znode的子节点的数量。
3.客户端和服务端交互
- NIO
channle、buffer、selector符合反应者设计模式,channle注册到selector中等待事件的发生,
数据的传输依靠channle并且是双向通道Input/Output,数据的读取和写入只能依靠buffer缓冲区来实现
使用了直接内存/堆外内存减少了一次核心态到用户态的拷贝效率高。
- 只读模式
当服务器集群一半以上的节点挂掉之后,不能进行写操作,但是可以做读操作,这时可以开启只读模式
1.服务器开启只读模式: -Dreadonlymode .enabled=true;
2.客户端调用开启只读模式: zkServer.sh start -r或者new ZooKee(... canBeReadOnly=true);
3.以上两步都开启了只读模式才会生效;
ps:每一个写操作,主服务器都需要获得一半从服务器的确认,才可以进行同步数据;
- closeSession
创建的临时节点,与客户端的生命周期绑定,进行quit的时候,调用zk.close();
客户端发送一个op.closesession操作给服务端;
服务端,先持久化事务,在deleteNode删除该临时节点;
- 客户端启动类
ZookeeperMain类初始化Zookeeper,初始化ClientCnxn,初始化SendThread 和 EventThread,初始化socket连接;
命令行解析为request,提交请求request并且包装成packet,加入outgoingqueue队列;
SendThread:连接socket;
发送数据:
从outgoingqueue队列取出数据并发送ConnectRequest给客户端;
将需要等待的结果ConnectResponse放入penddingQueue;
读取数据:
将pendingQueue中的Packet读取出来并且使用服务端返回的结果进行装配
接收watcher事件通知,并且把通知加入到waitingEvents队列中去
如果是同步请求则唤醒线程
如果是异步请求则将Packet加入到waitingEvents队列中
EventThread:从waitingEvents队列中获取数据并且remove掉(只能触发一次);
如果是watcher事件通知,触发绑定的watcher逻辑(自定义的watcher逻辑);
如果异步请求,则调用对应的异步回调函数;
- 服务端启动类
QuorumPeerMain解析配置进行设置属性,判断进入单机模式 或者 集群模式(根据servers的数量)
单机模式下
ZooKeeperServerMain,从snap快照文件里的内容加载到内存,设置请求处理器RequestProcessor;
ps:如果事务和快照数据不一致,会重新执行事务加载到内存。
创建socket,取出packet解析出request;
请求器处理链会从queue中取出request进行处理(责任链模式);
PrepRequestProcessor:获取客户端的请求,生成txn事务,并且修改节点属性,调用下一个处理器;
SyncRequestProcessor:获取请求,将txn同步到磁盘,并进行快照snapslop,调用下一个处理器;
FinalRequestProcessor:获取请求,触发watcher发送事件(NIOServerCnxn#process),构造response返回给客户端;
集群模式下
QuorumPeer#start,从snap快照文件里的内容加载到内存
ZAB协议
4.领导者选取
- 建立连接
为了防止机器之间重复建立连接,因此只允许sid大的服务器进行连接。
- 1,2,3会有组合(2,1)、(3,2)、(3,1)而不允许类似(1,2)这样的出现。
- 投票的数据结构
每个投票中包含了两个最基本的信息,所推举服务器的SID和ZXID,投票(Vote) :
- id:被推举的Leader的SID。
- zxid: 被推举的Leader事务ID。
- electionEpoch: 逻辑时钟,用来判断多个投票是否在同一轮选举周期中,该值在服务端是一一个自增序列,每次进入新一轮的投票后,都会对该值进行加1操作。
- peerEpoch: 被推举的Leader的epoch(周期)。
- state: 当前服务器的状态。
选票管理QuorumCnxManager:
- sendqueue:选票发送队列,用于保存待发送的选票。
- recvqueue:选票接收队列,用于保存接收到的外部投票。
- WorkerReceiver:选票接收器。其会不断地从QuorumCnxManager中获取其他服务器发来的选举消息,并将其转换成一个选票,然后保存到recvqueue中, 在选票接收过程中,如果发现该外部选票的选举轮次小于当前服务器的,那么忽略该外部投票,同时立即发送自己的内部投票。
- WorkerSender:选票发送器,不断地从sendqueue中获取待发送的选票,并将其传递到QuorumCnxManager中。
一旦连接建立,就会根据远程服务器的SID来创建相应的消息发送器SendWorker和消息接收器RecvWorker,并启动。
- 服务器状态
服务器具有四种状态,分别是LOOKING、FOLLOWING、LEADING、OBSERVING。
- LOOKING:寻找Leader状态。当服务器处于该状态时,它会认为当前集群中没有Leader,需要进入Leader选举状态。
- FOLLOWING:跟随者状态。表明当前服务器角色是Follower。
- LEADING:领导者状态。表明当前服务器角色是Leader。
- OBSERVING:观察者状态。表明当前服务器角色是Observer。
- 启动时的领导者选举
- 最开始自己先投给自己,然后告知其他服务器;
- 各自服务器,首先判断选举的轮数,在依次比较zxid事务号、myid服务器号;
- 统计投票,不断的判断是否有服务器可以成为leader;
- 选举成功改变各自服务器状态即可;
ps:主要是依靠zxid和myid进行选举;
- 运行时的领导者选举
当follow挂掉后:
- follow和leader会有心跳检测,leader会检测当票数少于一半就会进行shutdown,
- 然后设置自己为looking状态,其他的follow也会为looking状态,然后重新进行选举;
当leader挂掉后,一定会重新进行选举;
ps:其他服务器会将自己的状态改变为looking,并且开启只读状态;
新增一台服务器后,以当前leader为准;
- 选举流程图
5.基本应用
- 分布式锁思路
利用顺序临时节点和watch机制来进行实现;
当前节点只能监听前一个节点,当前一个节点不存在就可以加锁,否则会一直监听;
- 分布式配置中心
利用watch注册监听器,当增加、更新、删除事件发生可以及时更新缓存,保证配置可以及时更新;