Zookeeper学习笔记(中)

Zookeeper学习笔记(中)

Zookeeper的基本原理和基本实现

深入了解ZK的基本原理

ZK的一致性:

  • ZAB 协议: Zookeeper 原子消息广播协议

      ZK通过选举保证 leader 的高可用, 
    
      三个阶段:
      	* 发现:选举 leader
      	* 同步:Follower 或者 Observer 从 leader 中同步最新数据
      	* 广播:
    
      服务器角色:
      	* Leader:领导者, 所有更新操作通过 leader 进行
      	* Follower:跟随者, 有投票权, leader挂了之后有权作为竞选者 
      	* Observer:只能读取数据, 没有投票权, 不能参与竞选 leader
    
      服务器状态:
      	* LOOKING
      	* FOLLOWING
      	* OBSERVING
      	* LEADING
    
      集群通讯: id大的向小的发起连接
    
  • Leader的选举

      选举算法:
      	通过FastLeaderElection, tcp协议
      	触发时机:
      		1) 集群启动
      		2) Leader宕机
      	成为leader的因素 : zxid 和 myid
    
      	思考: 有3个全新节点组成一个集群,依次启动1,2,3, 哪个节点会成为 leader ?
    
      选举过程:
      	1) 每个节点状态为 LOOKING
      	2) 每一个server发出一个投自己的投票(myid+zxid)
      	3) 处理投票:将收到投票和自己投票相比(比较zxid,比较myid),有些节点修改投票,再次发送
      		比较zxid的意义:过半数即认为OK, 只要半数中的机器有一个在新的选举中, 即它会成为新leader将数据同步给其他人
      	4) 统计投票, 确定
      	5) 修改服务器状态
    
  • 各个节点数据的同步:

      leader选举之后, leader同其他节点进行同步,同步完成, leader才真正变为leader
      leader同超过一半follower同步结束才OK
      follower选举完成后尝试连接leader,带上自己最大的 zxid, 来确定数据同步的过程
    
      * 同步算法(DIFF):
    
      	适用于 follower 的最大事务id, 在 min , max 之间
      	1. 发送同步请求
      	2. 发送同步数据
      	3. 一旦过半follower完成同步,leader发送信号结束同步流程,即可以对外服务
    
    
      * 同步算法(TUNC):
    
      	适用于 leader 宕机, 恢复后作为 follower 加入, 其上有已经 proposal 的新事务, 其id > max
      	宕机之后如果 TRUNC 之后, 新集群已经有数据修改, 则需要通过 DIFF 将新修改发送给加入节点
      	简单说明步骤:
      		1) 新节点加入后收到 trunc 命令, 将只存在于新节点上的数据回滚
      		2) 确定是否新leader有新的提交,如有进行DIFF, 完成整个过程
      
      * 同步算法(SNAP):全量
      	适合宕机多时后恢复, 其id < min , 集群的事务日志文件已经有更新多个了
    
      
      * 获取同步后的数据	
    
      	读取前条用  sync 方法进行数据同步, 确保数据为最新
    
  • 以下为我在思考ZK选举原理过程中产生的一些问题, 有些还未完全获取到正确答案, 暂时提供出来供大家一起讨论

      1. zxid在一个主周期中是否肯定一致连续?
              是
      2. 无 leader 的状态时, 是否可以接受消息更新? 只有leader选举完毕才能再继续对外服务(读写)?
              无 leader 状态不可接受, 需要等待到 leader 选举完毕
      3. 如果某zxid被废弃后, 客户端如何得知? 此种情况不会让客户端得知消息已接收确认的消息 ? 
              客户端只有在消息被commit之后才会收到更新成功的确认
      4. 只经过proposal, 没有commit的数据不会返回给客户端?(前leader宕机,可能有些自己写入提交,但是别的节点还未获取的数据)这些数据会被认为是客户端正常提交的数据?
      5. 投票中每个节点都发给其他节点? 投票何时结束? 何时出发统计投票?
      6. 同步: observer 需要如何同步? 
      7. 同步是否有可能失败,导致重新选举 leader?
              有可能
      8. 个别follower无法连接leader导致无法完成同步, 重新回到选举状态, 之后如何? 会真正再次进行选举?
              会再次选举
      9. 最大,最小事务id:需要理解存储机制, min 不在快照文件中的最小id, max :事务日志中的最大id, 只要在事务日志中,即都是已经在集群中commit(个别节点上没有)
      10. 修改的proposal 是发给所有节点? 为何 diff 同步还得再发一份 ?
    

ZK的具体实现

  1. 客户端连接:

     多个地址会随机排序,从前往后继续
     查看类 : org.apache.zookeeper.client.StaticHostProvider
    
  2. 会话:

     客户端和服务端的一个连接是TCP连接
    
     会话实现类 : SessionImpl
     	Ticktime : 会话下次超时时间
    
     会话状态:
     	CONNECTING
     	CONNECTED
     	RECONNECTING
     	RECONNECTED
     	CLOSED
    
     会话的维护
     	* 服务器通过 SessionTracker 维护会话
     	* 会话检查和清理都在 Leader 节点处理
     	* 通过三个维度管理会话:
     		sessionId , session
     		失效时间, sessionSet
     		sessionId, 失效时间
    
     分桶策略: 用于会话失效检查和清除
     	分桶策略的说明:根据失效时间划分不同的session
     	把时间按照标准时间单位进行分割
     	某会话由于操作(心跳也是一个操作)导致超时时间变化, 从一个桶移到下一个桶
     	sessionTracker 中一个线程, 某次检查分桶中还有会话, 则说明超时
    
     会话清理的步骤
     	1. 状态设为 CLOSED
     	2. 所有节点发送会话关闭
     	3. 删除临时节点
     	4. 会话列表中移出
     	5. 网络断开
    
     会话重连
     	CONNECTION_LOSS
     	SESSION_EXPIRED
     	SESSION_MOVE
    
     待解决:
     	清除之后, 还是否可以通过设置 sessionId , 连接上一个session ? 连接时设置 sessionId 如何使用 ?
    
  3. 数据和存储:

     基本结构:
     	ZKDatabase
     		存储管理所有会话, datatree 的存储和事务日志
     		定期向磁盘写入快照数据
     		节点启动恢复内存数据
    
     	DataTree
     		维护数据/目录/权限
     		数据的领域模型
    
     	DataNode
     		树形中每个节点,包括父节点,子节点,节点数据
     
     事务日志:
     	日志文件:
     		datalog 或者配置的目录
     		vsersion-2 代表日志格式的版本号
     		大小64MB
     		文件名为后缀16进制格式,其中包含了本日志文件第一个 zxid
    
     	日志格式:
     		日志文件可以通过工具解析出: LogFormatter
     		日志格式内容解析:
     			各类会话,事件和内容都存储
    
     	日志写入:
     		通过 FileTxnLog 实现日志写入, 使用 append 方式
     		写入过程:
     			1. 确定事务日志文件,确定是否需要扩容(新增文件会用0填满64MB)
     			2. 事务序列化
     			3. 生成checknum
     			4. 写入文件流
     			5. fsync, 写入磁盘
    
    
     数据快照:
     	ZK中某个时刻的完整数据,和事务日志是不同的文件
     	文件后缀为快照中所包含的最新的zxid
     	通过SnapshotFormatter可以查看内容
    
     	快照流程:
     		1. 确定是否需要快照: 默认 snapCount 100000, 算法避免集群节点同时进行快照
     		2. 切换事务日志文件
     		3. 创建快照异步线程
     		4. 生成快照数据文件
     		5. 数据刷入快照文件
     
     事务日志和数据快照的联系和异同:
     	快照存储:
     		当时内存中的数据模型
     		一旦快照存储之后,会新开事务日志文件
    
     	事务日志:
     		所有 commit 提交的数据都会记录在事务日志中
     		事务日志文件
    
     	恢复使用:
     		取最新的快照文件, 恢复到内存数据结构中
     		最新的事务日志中有些数据未反应在快照中, 需要根据事务日志中的记录, 也恢复到数据结构中
原文地址:https://www.cnblogs.com/ownraul/p/5605223.html