zookeeper内部原理概要整理

请求,事务和标识符关系

  • 请求分为只读请求(exists,getData,getChildren)和写入请求(create,delete,setData).
  • 只读请求服务器自身会做处理,并且将信息返回给客户端。
  • 修改请求,zk服务器接到请求之后,会将该请求转发给群首,群首执行该请求,并且形成状态的更新,称为事务(transaction)。
    一个事务为一个单位,也就是所有变更操作都是原子方式执行的。事务包含两个重要属性(节点中新的数据字段值and该节点新的版本号)。处理事务就是将该事务的数据字段值与节点版本号赋值给原来节点。
  • 事务具有幂等性。也就是一个事务执行多次,得到的结果一致的。
  • 事务产生的时候,会有一个会话ID标识符(zxid)。zxid 时间戳epoch+计数器count组成的64位的整数。群首可以通过zxid 安排事务的执行顺序。zab的时候,可以通过该属性方便广播各个服务器的状态信息。

群首选举

  • 群首为集群环境中选举出来的服务器,如果自身不出意外会一直被集群认可,设置群首目的是为zookeeper状体变更请求进行排序,及通过事务zxid的顺序将事务分发给追随者,确保集群按照群首确定的顺序接收并且处理这些事务。
  • 使用仲裁模式(quorum)进行群首选举,仲裁模式要求服务器之间能够两两相交。
  • 每个服务器启动后都会进入looking状态,然后开始选举群首或者查找群首,如果群首已经存在,其他服务器就会通知新启动服务器。
  • 如果所有服务器都处于looking状态,则开始群首推举,通过信息交换对群首选择达成共识,群首进入leading装填,其他服务器进入following状态。
  • 流程: looking–>向每个服务器发送投票vote(sid,zxid),zxid最大的投票生效,zxid相等则sid 最大的投票生效。最后选举出zxid最大,sid相对较大的服务器为群首。 因为集群可能存在消息延时,消息故障之类的情况,所以一个服务器只要收到仲裁数量的同样的vote。标示选举就成功。

Zab:状态更新广播协议。

  • 辟如集群接收到一个setData请求的时候,追随者会将请求转发给群首。群首会试探性执行该请求,并且将执行结果以事务的方式对状态的更新进行广播。一个事务中包含了数据变更的确切操作,当事务提交的时候,服务器就会将变更信息反馈到DataTree上。
  • Zab(zookeeper原子广播协议)协议用来提交事务及确认事务的提交,假设存在一个活动的群首,并且有仲裁数量的追随者支持。
  • 群首向所有追随者发送一个提议(proposal)消息p –> 追随者发回一个ACK消息表示收到该提议 –> 仲裁数量的服务器确认消息之后,群首就发送消息给通知追随者提交事务(conmit)。
  • Zab保障了几个重要属性:
  • (1)如果群首按照顺序广播了事务T1和T2,那么每个服务器在提交T2事务之前保证事务T1已经提交完成
  • (2)如果某一个服务器按照T1-T2顺序提交了事务,那么其他服务器也必然按照T1-T2顺序提交的事务。
  • (3)一个被选举的群首必须提交完前面时间戳内需要提交的所有事务,才能开始广播新的事务。
  • (4)任何时间点都不会出现被仲裁支持的两个群首。

观察者

  • 观察者和追随者作用大概都差不多,不同的就是,不参加群首的选举。和追随者一样都用来提交群首的提议(proposal)

zookeeper的请求处理器介绍

  • PrepRequestProcessor: 接收客户端的请求并执行这个请求,处理结果是生成一个事务。如果是读请求,事务的成员属性为null。
  • SynRequestProcessor: 负责将事务持久化到磁盘。实际就是将事务按照顺序追加到事务日志中,并生成快照数据。
  • FinalRequestProcessor: 如果Request对象包含事务数据,该处理器则负责DataTree的修改,如果没有,则从数据树种读取数据并返回给客户端。
  • ProposalRequestProcessor:该处理器主要有处理以下三件事: (1)准备一个提议,并且将提议发送给追随者;(2)将请求转发给CommitRequestProcessor; (3)如果是写的操作请求,将请求也转发给SynRequestProcessor处理器。
  • AckRequestProcessor: 该处理器名字可以看出来是返回一个ack确认信息,并且返回给自己。群首需要需要收到每个服务器的确认信息,包括自己,所以该处理器只在群首服务器中使用。
  • CommitRequestProcessor: 该处理器会收到足够多的确认消息,并进行提交。leader.processAck()方法会将提交的请求加入由该处理器的处理对列中。
  • ToBeAppliedRequestProcessor:每一个更新请求,会存放到一个待接受请求列表,FinalRequestProcessor,执行这些请求, ToBeAppliedRequestProcessor 则将待接受请求列表的已执行记录移除。
  • FollowerRequestProcessor: 该处理器接收并且处理客户端的请求,然后将请求转发给CommitRequestProcessor,如果是写的请求则同时转发给SynRequestProcessor。
  • 以上处理器为zookeeper工作主要处理器。在集群中的角色不一样,也会使用不同的处理器。

zookeeper会话

  • 会话(session)是zookeeper的一个重要抽象,保证请求有序,临时znode节点,监视点等都与会话密切相关。zookeeper服务器一个重要任务就是跟踪并且维护这些会话。
  • 会话提供了顺序的保障,一般一个客户端只会打开一个会话,客户端请求一FIFO顺序执行。
  • 保证会话存活:在仲裁模式下,群首服务器发送一个ping给追随者,追随者返回最新一次ping之后的一个session列表。群首服务器每半个tick发送一个ping给追随者们。

监视点

  • 监视点由读取操作设置的一次性触发器,每个监视点由一个特定操作触发。
  • 服务器端使用WatchManager类负责管理当前已经被注册的监视点列表,并负责触发他们。所有类型服务器,对监视点处理都是这个方式。
  • DataTree类中,当处理读请求的时候,该类就会吧监视点添加到WatchManager,当处理一个事务的时候,则判断是否要触发响应的监视点。
  • 在服务器端触发的监视点,最终会传播给客户端,负责处理传播的为客户端的ServerCnxn类。此对象实现了watcher的process方法。
  • 监视点只保存在内存中,不会持久化到硬盘。客户端与服务器端断开的时候,监视点会移除,因为客户端库也会维护一份监视点数据,当重新连接之后,再次被同步到服务器端。

客户端

  • zookeeper与ClientCnxn是客户端主要的类。zookeeper实现了大部分的API。写客户端的时候需要实例化该类建立一个会话。zk.create(…)
  • ClinetCnxn管理连接到server的socket,该类维护了一个可连接的zookeeper服务器列表。并且断掉连接的时候可以无缝连接到其它服务器,使用同一个会话(会话没有过期情况下),并且将监视点同步到新连接的服务器。

主从模式下一般的任务流程

  • 1.管理权变化。 zk.exist(“/master,…”)
  • 2.主节点等待从节点列表的变化zk.getChildern(“/worker”,…)
  • 3.主节点等待新任务进行分配。 zk.getChildren(“/task”,…)
  • 4.从节点等待分配新任务。 zk.create(“workers/worker-“+id ,…)
  • 5.客户端等待任务执行结果的。通过监视点状态的变化,实现callback类的processResult方法。

小结

  • zookeeper 使用的jute进行序列化。
  • 群首竞选机制是高可用性的保障。使用Zab来传播状态,使整个集群对外状态保持一致。
  • 定义观察者服务器可以提高读取操作吞吐量,不影响写操作的吞吐量。但是不能增加系统的可用性,因为观察者不参与群首选举。