zookeeper

馋奶兔 提交于 2020-10-29 01:23:08

一、What

  • 一个主从架构的分布式框架
  • 给分布式框架提供协调服务(service)

作用

  • 提供简版文件系统来存储数据
  • 维护和监控存储的数据状态变化,通过监控数据状态变化达到基于数据的集群管理
  • 主要用来解决分布式集群中应用系统的一致性问题

应用场景

1. 主备切换
2. 节点的上下线感知
3. 统一命名服务
4. 状态同步服务
5. 集群管理
6. 分布式应用配置管理

二、基本概念

ZooKeeper=简版文件系统(Znode)+原语+通知机制(Watcher)

  • ZK文件系统 基于类似于文件系统的目录节点树方式的数据存储
  • 原语 提供类linux指令进行操作
  • Watcher(监听器)

数据节点ZNode

数据节点本质就是目录

持久节点 临时节点
非有序节点 create create -e
有序节点 create -s create -s -e
  1. 持久节点(无序) 节点创建以后,即便连接断开,除非主动删除,不然会一直存在
  2. 持久节点(有序) 创建节点的时候加上 -s ,会默认的在目录后加上数字 防止同一目录创建同名ZNode导致失败
  3. 临时节点(无序) 节点创建以后,一旦连接断开会自动删除 创建节点的时候通过 -t 指定
  4. 持久节点(有序) 同上

会话

与zk交互时会建立TCP长连接,称为会话 建立会话后,如果超过SessionTimeout时间,两者间没有通信,会话超时 特点:同一个会话里执行的指令是有序的;不同会话之间的指令是无序的

事务zxid

  • 每个对数据的增删改操作都会生成一个对应的zxid
  • zxid全局唯一,并且是自增的
  • zxid通常是由64位数字,epoch+counter组成

Watch监视与通知

方式一(轮询):ZooKeeper以远程服务的方式,被客户端访问;客户端以轮询的方式获得znode数据,效率会比较低(代价比较大)

方式二(通知机制):客户端在znode上注册一个Watcher监视器,当znode上数据出现变化,watcher监测到此变化,通知客户端

  1. Watch作用流程
  • 客户端在服务器端,注册的事件监听器
  • watcher用于监听znode上的某些事件
  • 比如znode数据修改、节点增删等
  • 当监听到事件后,watcher会触发通知客户端
  1. 节点上下线原理 ① 节点1(client1)创建临时节点 ② 节点2(client2)在临时节点,注册监听器watcher ③ 当client1与zk集群断开连接,临时节点会被删除 ④ watcher发送消息,通知client2,临时节点被删除的事件 **用到的zk特性:**Watcher+临时节点 好处:通过这种方式,检测和被检测系统不需要直接关联(如client1与client2),而是通过ZK上的某个节点进行关联,大大减少了系统耦合

三、HDFS HA方案

工作原理

ZooKeeper使用原子广播协议叫做Zab(ZooKeeper Automic Broadcast)协议

  • Zab协议有两种模式
    • 恢复模式(选主):因为ZooKeeper是主从架构;当ZooKeeper集群没有主的角色leader时,从众多服务器中选举leader时,处于此模式
    • 广播模式(同步):当集群有了leader后,客户端向ZooKeeper集群读写数据时,集群处于此模式
  • 为了保证事务的顺序一致性,ZooKeeper采用了递增的事务id号(zxid)来标识事务,所有提议(proposal)都有zxid

监听器

  • 注册:客户端向ZooKeeper集群注册监听器

  • 监听事件:监听器负责监听特定的事件

  • 回调函数:当监听器监听到事件的发生后,调用注册监听器时定义的回调函数

HA原理

HDFS HA方案,主要分两部分:

①元数据同步

  • 在同一个HDFS集群,运行两个互为主备的NameNode节点。
  • 一台为主Namenode节点,处于Active状态,一台为备NameNode节点,处于Standby状态。
  • 只有Active NameNode对外提供读写服务,Standby NameNode会根据Active NameNode的状态变化,在必要时切换成Active状态。
  • JournalNode集群
    • 在主备切换过程中,新的Active NameNode必须确保与原Active NamNode元数据同步完成,才能对外提供服务
    • 所以用JournalNode集群作为共享存储系统;
    • 当客户端对HDFS做操作,会在Active NameNode中edits.log文件中作日志记录,同时日志记录也会写入JournalNode集群;负责存储HDFS新产生的元数据
    • 当有新数据写入JournalNode集群时,Standby NameNode能监听到此情况,将新数据同步过来
    • Active NameNode(写入)和Standby NameNode(读取)实现元数据同步
    • 另外,所有datanode会向两个主备namenode做block report

②主备切换

  • 每个NameNode节点上各有一个ZKFC进程
  • ZKFC即ZKFailoverController,作为独立进程存在,负责控制NameNode的主备切换
  • ZKFC会监控NameNode的健康状况,当发现Active NameNode异常时,通过Zookeeper集群进行namenode主备选举,完成Active和Standby状态的切换
    • ZKFC在启动时,同时会初始化HealthMonitor和ActiveStandbyElector服务
    • ZKFC同时会向HealthMonitor和ActiveStandbyElector注册相应的回调方法(如上图的①回调、②回调)
    • HealthMonitor定时调用NameNode的HAServiceProtocol RPC接口(monitorHealth和getServiceStatus),监控NameNode的健康状态,并向ZKFC反馈
    • ActiveStandbyElector接收ZKFC的选举请求,通过Zookeeper自动完成namenode主备选举
    • 选举完成后回调ZKFC的主备切换方法对NameNode进行Active和Standby状态的切换

主备切换过程 ① 启动NameNode,ZKFC,此时两个NameNode的状态都是竞选状态 ② 两个ZKFC分别通过ActiveStandbyElector发起NameNode的选举 通过zookeeper的写一致性以及临时节点来实现 ③ 发起主备选举的时候,ActiveStandbyElector会尝试在zookeeper的某个目录下创建一个临时节点,zookeeper的写一致性会保证只有一个节点创建成功 ④ 创建成功的ActiveStandbyElector通过回调方式通知ZKFC,将对应的NameNode切换为Active状态;创建失败的也通过同样方式将NameNode切换为Standby状态 ⑤ 无论是否创建成功,这些ActiveStandbyElector都会监听那个目录; 当Active NameNode对应的HealthMonitor监控到NameNode异常时,会告知ZKFC ZKFC通过ActiveStandbyElector删除所创建的临时节点,以及目录A 若是ZKFC是因为异常断开的连接,那么目录A还会存在 ⑥ 此时处于Standby的NameNode会监控到这个消息 它首先会通过判断目录A是否存在来确认情况 如果是正常关闭的,则发起主备选举,成功创建临时节点,并且将NameNode的状态切换为Active 如果是异常关闭的,则会 (1) 通过RPC调用,试图让之前的Active的NameNode切换为StandBy (2) 隔离:① 发送kill指令 ② 使用hadoop的隔离方式

脑裂 在分布式系统中双主现象又称为脑裂,由于Zookeeper的“假死”、长时间的垃圾回收或其它原因都可能导致双Active NameNode现象,此时两个NameNode都可以对外提供服务,无法保证数据一致性

  • 隔离 对于生产环境,这种情况的出现是毁灭性的,必须通过自带的**隔离(Fencing)**机制预防此类情况
  • 原理
    • ActiveStandbyElector成功创建ActiveStandbyElectorLock临时节点后,额外创建一个ActiveBreadCrumb持久节点
    • ActiveBreadCurmb持久节点保存Active NameNode的服务器信息
    • 当Active NameNode正常状态下断开与Zookeeper Session,会一并删除临时节点ActiveStandbyElectorLock和ActiveBreadCurmb持久节点
    • 如果是异常断开的,那么此时临时节点ActiveStandbyElectorLock不存在,但是ActiveBreadCurmb持久节点还存在;Standby节点会收到监听器发来的提醒将要从Standby切换成Active时,会先通过ActiveBreadCurmb里的服务器信息做隔离 1、通过RPC调用,试图让之前的Active的NameNode切换为StandBy 2、隔离(hadoop提供这两种隔离):① 发送kill指令 ② 使用hadoop的隔离方式

只有成功地fencing之后,选主成功的ActiveStandbyElector才会回调ZKFC的becomeActive方法transitionToActive将对应的NameNode切换为Active,开始对外提供服务

四、Zookeeper架构

ZooKeeper服务器四种状态:

  • looking:服务器处于寻找Leader群首的状态

  • leading:服务器作为群首时的状态

  • following:服务器作为follower跟随者时的状态

  • observing:服务器作为观察者时的状态

安其内

全新leader选举(重启集群) 原则:集群超过半数的服务器启动后,才能选出leader 选举规则: 1. 初始化:每个节点都投自己一票,然后向其他所有节点发送自己票的信息 2. 交换投票信息:接收其他节点的选举信息,与自己的那票进行比较 投票信息vote信息结构为(serverId, zxid) 会先比较zxid,zxid大的那台服务器胜出(zxid越大意味存储了更多的数据) 若是zxid相同则serverId大的那台服务器会胜出 比较结束后,每台服务器会更新自己的投票信息,继续给其他服务器发送 3. 决定投票结果:假设服务器B接收到超过半数的票选举自己,则自己从looking切换为leading,其他服务器从looking切换为following 4. 当服务器C启动时,发现已有Leader,不再选举,直接从Looking改为Following

全新leader选举(leading挂掉或者出现网络分区)

攘其外

读操作

  • 常见的读取操作,如ls /查看目录;get /zktest查询ZNode数据
  • 读操作
    • 客户端先与某个zk任意一台服务器建立Session
    • 然后,直接从此ZK服务器读取数据,并返回客户端即可
    • 关闭Session

写操作 ① 与zk任意一台服务器建立Session建立连接 ② follower将写请求转发给leader(不执行) ③ leader收到消息后,发起proposal提案 ④ 每台服务器都会收到proposal后会记录这次操作并向leader返回同意,但不执行 ⑤ 超过半数quorum同意,则leader提交commit提案,leader执行该操作 ⑥ leader通知所有节点也commit提交该提案;所有节点在所在服务器执行该操作 ⑦ client连接的那台follower响应Client

五、原理

quorum仲裁

  • 什么是仲裁quorum?
    • 发起proposal时,只要多数派同意,即可生效
  • 为什么要仲裁?
    • 多数据派不需要所有的服务器都响应,proposal就能生效
    • 提高集群的响应速度
  • quorum数如何选择?
    • 集群节点数 / 2 + 1

为什么集群节点数强烈建议奇数个? 5节点的比6节点的集群

  • 容灾能力一样,
  • quorum小,响应快
  • 偶数个如果被网络分区平分,则不能提供服务 ####网络分区和脑裂
    • 网络分区:网络通信故障,集群被分成了2部分
    • 脑裂:
      • 原leader处于一个分区;
      • 另外一个分区选举出新的leader
      • 集群出现2个leader

ZAB算法

  1. 每个节点都有一个计时器(150毫秒~300毫秒之间的随机数),时间到后会发起选举的操作

  2. 非leader节点会定时的向leader节点发送心跳,leader收到心跳后会返回信息,非leader节点收到这个信息后会重置计时器

  3. 当发生网络分区并且leader处于少数的那边时,其他follower会进行选举,假设B服务器选举成功,那么它会和其他follower进行zxid的比较

    ① 假设服务器C的zkid比服务器B的少10个,那么服务器B会将这十个操作封包发给服务器C来执行 ② 假设服务器D的zxid比服务器B多10个,那么服务器B会让它将这十个记录都删除 做完以上操作后,服务器B才会从follower切换成leader,并且将epoch里的值进行加1,zxid进行重置

  4. 当网络恢复了之后,旧leader比较路径下的epoch值的时候,发现已经有新的leader产生,会将自己切换成follower,并且进行数据的同步

状态同步

完成选举后,zk之间会进行状态同步操作

  1. leader构建NEWLEADER封包,包含leader最大zxid值,广播给其他follower
  2. follower收到后会跟自己最大zxid比较,若是比它小则进行同步操作
  3. leader给每个需要同步的follower创建LearnerHandler同步线程,负责进行同步操作
  4. leader主线程等待LearnerHandler线程处理结果
  5. 只有当大部分follower完成同步,该集群才对外提供服务,相应写请求
  6. LearnerHandler线程处理逻辑
  7. 接收follower封包FOLLOWERINFO,包含此follower最大zxid(代称f-max-zxid)
  8. f-max-zxid与leader最大zxid(代称l-max-zxid)比较
  9. 若相等,说明当前follower是最新的
  10. 另外,若在判断期间,有没有新提交的proposal
    1. 如果有,那么会发送DIFF封包将有差异的数据同步过去.同时将follower没有的数据逐个发送COMMIT封包给follower要求记录下来.
    2. 如果follower数据id更大,那么会发送TRUNC封包告知截除多余数据.
    3. 如果这一阶段内没有提交的提议值,直接发送SNAP封包将快照同步发送给follower.
  11. 以上消息完毕之后,发送UPTODATE封包告知follower当前数据就是最新的了
  12. 再次发送NEWLEADER封包宣称自己是leader,等待follower的响应.

分布式锁

  1. 所有需要获取锁的引用都在 /locker路径下创建一个有序临时节点
  2. 该路径下序号最小的应用获取该锁,其他应用分别监视比自己小一点的那个ZNode
  3. 当序号最小的那个应用操作完后,断开连接时,比它稍微大一点的那个应用会获取锁

分布式锁主要是应用了zk的一致性、临时节点、watch监视器这几个特性来保证的

WHO

  1. NameNode使用ZooKeeper实现高可用.
  2. Yarn ResourceManager使用ZooKeeper实现高可用.
  3. 利用ZooKeeper对HBase集群做高可用配置
  4. kafka使用ZooKeeper(仅限0.9以及之前版本)
    • 保存消息消费信息比如offset.
    • 用于检测崩溃
    • 主题topic发现
    • 保持主题的生产和消费状态
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!