zookeeper watcher功能分析

来源:互联网 发布:lol进游戏无法连接网络 编辑:程序博客网 时间:2024/06/05 00:51

0. 总述

All of the read operations in ZooKeeper - getData(), getChildren(), and exists() - have the option of setting a watch as a side effect. Here is ZooKeeper's definition of a watch: a watch event is one-time trigger, sent to the client that set the watch, which occurs when the data for which the watch was set changes。

getData,getChildren(),exists()这三个方法可以针对参数中的path设置watcher,当path对应的Node 有相应变化时,server端会给对应的设置了watcher的client 发送一个一次性的触发通知事件。客户端在收到这个触发通知事件后,可以根据自己的业务逻辑进行相应地处理。

注意这个watcher的功能是一次性的,如果还想继续得到watcher通知,在处理完事件后,要重新register。

1. 客户端

zookeeper watcher功能分析 - teddy621 - 我的博客


整个类设计可以分为三大类:

1)黄色部分表示处理Watcher功能
2)绿色部分表示nio处理中一个数据结构的封装
3)蓝色部分表示通过nio请求,接收数据的处理类


 

请求的处理流程:

  下面以 public byte[] getData(final String path, Watcher watcher, Stat stat)接口为例进行说明:

      1. 在有Watcher的情况下,

      对照右上图的类图,把watcher,path包装成一个DataWatchRegistration对象。整个WatchRegistration的结构主要是利用了继承的多态性,不同子类的getWatches方法返回不同的结果集,而这些对调用方是屏蔽的。

  2. 构造请求头

          不同方法请求头的主要区别是type不一样 

  3. 构造request,reponse

       从类图中也可以看出,不同的方法对象于不同的request,response对象,对getData方法来说,是对应于 GetDataRequestGetDataResponse

     GetDataRequest对象中的属性是:path,以及一个boolean watch用来表示这个请求是否有watch

  4. 构造响应头

        这时候,ReplyHeader还是一个空对象,这里的内容要等nio返回内容时进行填充。

5. 构造 Packet对象

       包含:RequestHeaderReplyHeaderrequest,response, watchRegistration

      在这里把 requestHeader,request对象进行序列化,放入packet对象的Bytebuffer属性中。

     replyHeader,response对象目前都是空,内容要等nio返回内容,解释出Bytebuffer中内容进行填充。

      同时在一开始就构造的DataWatchRegistration对象赋值给packet对象中的watchRegistration属性,这属性会在收packet对象时有作用,下面再介绍。

6. packet对象准备好后,把整个对象放入一个outgoingQueueLinkedList中,就等着通过niopacket对象中byteBuffer中的内容发送给server端面。之所以使用LinkedList,是因为它提供了操作头,尾的方法。

7. packet被放入outgoingQueue中,等待SendThreadpacket对应的内容发送给server

8.如果是带callback的异步调用,则整个调用过程就结束,如果是同步调用的话,判断packetfinished状态是否为true,如果为false,进行wait,等待nio得到response后,把packet的状态改成finishedtrue,调用notify通知当前等待。

    后续NIO操作,server进行数据传输就交给专门的SendThread来处理。

   整个nio主要是是围绕SelectorSocketChannelSelectionKey,ByteBuffer这四个对象进行操作。

  在这里围绕主流程来描述:

    1. SelectionKey 处于isWritable状态时

       A outgoingQueue中取出一个packet中的byteBuffer内容,写入socketChannel

       BoutgoingQueueremove 第一个packet,

      C如果这个packet对应的头不是ping, auth类型,把这个packet放入pendingQueue。因为这两个请求的返回不需要额外处理,因此也就不需要放在等待返回的对列中。   pendingQueue的作用就是:因为采用了NIO返回是异步的,当结果返回时,要能找到原来请求的对象,所以要维护这么一个列队来保存已经被发送,但还没收到返回的Packet对象。

 

响应的流程分析

   响应的数据类型:

    sendthread接收来自serverresponse类型:

1)针对心跳的ping请求的resp

2)针对auth请求的resp

3)一般接口请求的resp

4)如果接口请求要求了watcher,当watcher关注的内容有变化时的notification

 

一般接口请求的resp处理:

1) 判断从ByteBuffer中反序列化出来的replyHeader中的xid pendingQueue中第一个packet维护的xid是否相同。Zookeeper是保证发发送的packet会发收到response,在这里是对这个有序性进行验证。

2) 反序列化出 response对象的内容。

3) 这个时候已经拿到了packet的响应内容,但为了对callbackwatcher功能的支持,还需要额外的处理:

4) A:如果这个packet包含了watcher,将这个请求对象的watcher注册到watcherManager,这是为了当针对watchernotification响应到达的时候,能找到对应的watcher

5) B: 如果接口是同步调用的话,这时设置packetfinishedture,并且通过notify进行通知。

6) C: 如果是带callback的异步调用,则将packet放入eventThread,让eventThread异步调用callback接口。

 

针对watcher对应notificationresp处理

如果sendthread分析出当前的response是针对watchernotification

1) reponse反序列化成WatcherEvent

2) WatcherEvent转化成WatchedEvent

3) 之前说过如果请求带watcher,在返回时,会在watcherManager中注册对应的watcher。当收到WatchedEvent后,就可以根据event的数据从watcherManger中取到对应的watcher集合。

4) WatchedEvent和对应的watcher集合封装成WatcherSetEventPair

5) WatcherSetEventPair放入eventThread中的waitingEvents列表

6) eventthreadrun循环中,取中WatcherSetEventPair,调用其中的watcher接口。

 

二、           服务端针对watcher设计

zookeeper的数据模型如下:

zookeeper watcher功能分析(2) - teddy621 - 我的博客

按照经验,在server端 应该设计成这样:

1) 应该有一个domain来 对应这个数据模型

2 domain中 包含了一些监听对象,也就是说在某个node上修改数据时,要调用相应的各类监听对象,把此node的数据被修改的事件告诉监听对象,至于 监听对象想做些什么操作,应该由它自己来决定,这点应该和这个domain无关。根据One-time trigger的特性,这些监听对象被触发后需要被 remove掉。

3) 这些监听对象应该在client调 用getData(),getChildren(),exits()方法时,registerdomain中。

实际实现:

DataTree 来表示整个数据模型,其中包含了WatchManager

当有node数 据变更,node增加,删除时,调用 triggerWatch方法,根据path来判断是否有对应的watcher,如果有watcher,依次调用这些watcherprocess方 法。

注意这里的watcher并不是客户端通过getData()等方法registerwatcher。而是server端的 NIOServerCnxn,这类是在server端 实现Watcher接口。在实现上是每个客户端连接一个NIOServerCnxn。 当客户端请求的requestwatcher参 数为true时,在server端把NIOServerCnxn注册到watcherManager

process接口的实现很简单,

ReplyHeader h = new ReplyHeader(-1, -1L, 0);

if (LOG.isTraceEnabled()) {

ZooTrace.logTraceMessage(LOG, ZooTrace.EVENT_DELIVERY_TRACE_MASK,

"Deliver event " + event + " to 0x"

+ Long.toHexString(this.sessionId)

+ " through " + this);

}

// Convert WatchedEvent to a type that can be sent over the wire

WatcherEvent e = event.getWrapper();

//6+1 10-07-04

System.out.println("****server 6+1:"+event.getType());

sendResponse(h, e, "notification");

1)首先构造响应头,这里xid,zxid都 是固定的-1,用来表示这是一个event reply

2把接口中的WatchedEvent对 象转换成WatcherEvent,这是因为WatchedEvent中 使用了enum,直接转换成 int.

3)reply header, WatcherEvent写入输入流。replyheader的作 用是让client来判断当前是接收的是一般的packet还 是event通 知,WatcherEvent包 含了事件的类型,因为我们知道process接口中是 WatchedEvent对象,因此可以肯定client在拿到WatcherEvent对象后,还要还原回来的WatchedEvent。 再调用在 client中存在的Watcher实 现类。

个人总结:

1)  在使用nio进行异步通信时,有一个队列:sendingQueue来存放当前请求的packet,设计一个和server通信的线程,当socketChannel可写的时候,从队列出取出packet,发送给server。为了当server返回请求结果时,能找到原来的packet,还需要有一个pendingQueue来存放已对发送给server但还没有接收到 response packet。简单来说就是一个负责通信的Thread外加两个队列的配合来完成发送请求,接收响应的业务。

2)  如果需要像zookeeper一样设计watcher功能,则需要在client有一个watcherManager来存放对应请求关心的watcher,当packet正常返回时,把watcher注册到manager中去,等watch事件触发时,能有地方找到对应的watcher,并调用规定的接口。

3)  Server端对watch事件的触发,和平时经常遇到的listener事件触发是一模一样的:实现一个事件总的管理类:listenerManager,它包含了各个listener。在需要关心的接口调用地方,增加调用事件管理类的接口,比如:process()。具体对这个事件有什么操作,由事件管理类中的各个listener自己去实现。

4)  为了事件处理的异步,zookeeper另外设计了EventThreadSendThread在分析response时得知可以异步处理时,把数据封装成一个特定对象交给EventThread

5)  所谓的异步callback,异步watcher都可以在同步实现中找到影子:spring中的对ibatis,hibernate进行封装提供的callback功能,listener事件管理的设计。

 

 

 

 

0 0
原创粉丝点击