Zookeeper内部实现分布式数据一致性(底层系统模型)(二)

承接上一篇:继续介绍Watcher部分:

<4> Watcher :数据变更的通知:

       zk提供了分布式数据的发布/订阅功能:当主题对象自身状态变化是,会通知所有订阅者;

       在zk中引入Watcher机制来实现这种分布式通知功能;ZK允许客户端向服务端注册一个Watcher监听,当服务端的一些指定的事件触发了这个Watcher,那么就会向客户端发送一个事件通知 来实现分布式通知功能;

  (1) ZK中的Watcher机制包括客户端线程,客户端Watcher和ZK服务器三个部分;简单来讲器其工作流程:    

      

     客户端向ZK服务器注册Watcher的同时,会将Watcher对象存在客户端的WatcherManager中,当ZK服务器触发Watcher事件后,会向客户端发送通知,客户端线程从WatchManager对象中取出对于的Watcher对象来执行回调逻辑;

     <1> 介绍一下Watcher接口:

      标准的事件处理器,包含了两个枚举类,KeeperState和EventType两个枚举类。分表代表事件的通知状态和类型。

        同时也定义了事件回调方法:process(WatchedEvent event)

        WatchedEvent封装了通知事件,包含了每一个事件的三种基本属性:keeperState(通知状态),eventType(事件类型),path(节点路径);

        ZK使用WatchedEvent对象来封装服务端事件并传递给Watcher,从而方便回调方法process对服务端事件进行处理。

      1)关于WatcherEvent和WatchedEvent:

        服务端生成WatchedEvent事件之后,会调用getWrapper方法将自己包装成一个可序列的WatcherEvent事件,以便网络传输。

              注意点:客户端无法从事件通知中获取对应数据节点的原始数据内容以及变更后的新数据内容,而是需要客户端再次去重新获取数据。

     

      <2> 工作机制

        三个过程:客户端注册Watcher,服务端处理Watcher,客户端回调Watcher;

        在ZK源码中:

          Zookeeper的构造方法中,可以传入一个Watcher接口的实例。该接口实例需要实现process方法,也就是客户端执行回调的逻辑;

          除此之外,ZK客户端也可以通过getData,getChildren,exists三个接口向Zookeeper服务器注册Watcher。

            强调:这些接口的源码,可以去官网下载查看,以及内部处理流程;

        1)Watcher的基本注册逻辑;

            针对getData接口:getData(final Strring path,Watcher watcher,Stat stat);

              首先将Watcher包装成WatchRegistration,接着又被包装成Packet,放入到客户端的发送队列。(Packet是zk最小的通信协议单元)

              Zookeeper客户端向服务端发送这个请求之后,同时就会等待这个请求的返回。

              完成请求发送后,客户端的SendThread线程接收来自服务端的响应,finishPacket方法从Packet取出对应的Watcher并注册到ZkWatchManager中去;

              在register方法中,客户端会将暂时保存的Watcher对象转给ZkWarcherManager,并最终保存在ZKWatcherManager.dataWatches中(dataWatches是一个

                Map<String,Set<Watcher>>,用于将数据节点的路径和Watcher对象进行一一映射后管理起来)。

            客户端每一次调用getData接口,这个Watcher实体并不是都发送给客户端。

               在WatchRegisteration封装到Packet对象中,在底层实际的网络传输序列化中,并没有将WathRegisteration完全序列化到底层字节数组中去;而只会将RequsetHeader和request两个属性序列化,可以在Packet.creatreBB的方法中查看源码得到验证。

        2)服务端处理wacher

            服务端采用ServerCnxn存储Watcher,ServerCnxn是一个Zookeeper客户端到服务端之间的连接接口。ServerCnxn默认实现是一个NettyServerCnxn;

            数据节点的节点路径和ServerCnxn最终被存储在WatchManager的wtcherTable和watch2Paths中。WatchManager是服务端Watcher的管理者。

              Watcher在服务端的触发逻辑:

                <1> 在数据节点更新后,服务端封装WatchedEvent(通知状态,事件类型,节点路径)

                <2> 根据数据节点从watchTable中取出对应的Watcher,watchTable和watch2Paths中将Watcher删除,满足触发一次性;

                <3> 调用<2>中找到的所有Wathcer的process方法。

                    如果需要注册Watcher的请求,zk会把当前请求对应的ServerCnxn作为一个Watcher进行存储,方法的具体:

                          在请求头标记“-1,表明当前是一个通知  /   将WatchedEvent包装成WatcherEvent以便进行网络传输序列化  /   向客户端发送该通知;

        3)客户端回调Watcher;

            由线程SendThread处理,将事件传给EventThread线程;

            <1> 接到请求之后,首先会将字节流转换成WatcherEvent对象。

            <2> 处理chrootPath;(后面祥讲);

            <3> 还原WatchedEvent;

            <4> 回调WatchedEvent,将WatchedEvent交给EventThread线程,在下一轮询周期回调;

            细节:客户端识别出事件类型后,会从相应的Watcher存储去除对应的Watcher,而客户端将遍历到的所有Watcher放入对队列,EventThread的run方法对该队列进行串行处理(这才是正真process执行,也就是会掉逻辑的实现。

          

猜你喜欢

转载自www.cnblogs.com/startelk/p/11519566.html
今日推荐