libthrift0.9.0解析(五)之TNonblockingServer&THsHaServer

本文是一边看代码一边写的,是真随笔,随看随下笔。

看TNonblockingServer,先看其父类AbstractNonblockingServer。一般来说,父类封装的都是通用的东西,具体的底层实现方式交由子类来实现。因此抽象类一般会作为两层之间的交点所在,父类在上层,子类在下层。先看父类,再看子类,先看高层,再看低层,先看框架再忽略底层细节,遇到一些细节非常想看想深入进去时,也会忍住不看,待到上层了解完毕之后,再回过头来看。必要先在头脑中形成一个坐标系,然后再往其中安放具体物件。不知这样对也不对【注:此方法不一定好,最好是单步调试跟入代码。11.11】。

AbstractNonblockingServer父类为Tserver,实现了serve方法:

public void serve() {
    // start any IO threads
    if (!startThreads()) {
      return;
    }
    // start listening, or exit
    if (!startListening()) {
      return;
    }
    setServing(true);
    // this will block while we serve
    waitForShutdown();
    setServing(false);
    // do a little cleanup
    stopListening();
  }

 startThreads方法开启一个线程,处理所有通道(连接)的所有请求,交由子类实现;startListening方法为通用方法,执行监听:serverTransport_.listen()。

看其子类TNonblockingServer中startThreads的实现:利用serverTransport_(必为TNonblockingServerTransport类型)构造一个SelectAcceptThread线程,然后开启。到此为止,服务开启完毕。

 

下面看SelectAcceptThread的实现,抽象父类为AbstractSelectThread,直接继承自Thread类。

按照上述原则,看AbstractSelectThread的构造函数和run方法。构造函数初始化了一个selector,没有run方法,其它很多方法,略去不看先。接着看子类SelectAcceptThread。

构造函数中把serverTransport注册到selector中,注册事件为accept。run方法主要代码:

while (!stopped_) {
          select();
          processInterestChanges();
}

 看其字面,select处理具体事件,processInterestChanges处理其它一些事务。

在select函数中,用handleAccept、handleRead、handleWrite处理各种事件,具体怎么处理,不用管先。

在processInterestChanges函数中,遍历Set<FrameBuffer>列表selectInterestChanges,对每个FrameBuffer调用其changeSelectInterests方法。ok,这轮完毕。

 

在handleAccept中,accect一个client,然后client在selector上注册read事件,然后根据client, clientKey生成一个FrameBuffer对象,把该对象绑定到clientKey上。

在handleRead中,取出绑定的FrameBuffer对象buffer,调用buffer的read方法从通道中读取数据,读取完毕后调用requestInvoke方法对该buffer进行处理,怎么处理的?等会儿再看。

在handleWrite中,调用绑定frameBuffer的write方法。

这轮结束,可以看出,其核心逻辑集中在FrameBuffer对象中。

 

看FrameBuffer构造函数,只是对传入的client,clientKey,selectorThread进行保存;

上轮的requestInvoke方法调用的是其invoke()方法,在invoke方法中,调用Processor对请求进行处理,处理完毕后调用responseReady方法和requestSelectInterestChange方法通知处理完毕,可进行下一步操作。

以下是responseReady的注释:

  /**
     * After the processor has processed the invocation, whatever thread is
     * managing invocations should call this method on this FrameBuffer so we
     * know it's time to start trying to write again. Also, if it turns out that
     * there actually isn't any data in the response buffer, we'll skip trying
     * to write and instead go back to reading.
     */

对注释的注释,也即当请求处理完毕后,检查 frameBuffer中的response,若有返回值,把frameBuffer当前状态置为准备写的状态(AWAITING_REGISTER_WRITE),否则,置为读的状态,可以继续读取下一条。

下面看changeSelectInterests方法,还是直接上代码比较明了:

  /**
     * Give this FrameBuffer a chance to set its interest to write, once data
     * has come in.
     */
    public void changeSelectInterests() {
      if (state_ == FrameBufferState.AWAITING_REGISTER_WRITE) {
        // set the OP_WRITE interest
        selectionKey_.interestOps(SelectionKey.OP_WRITE);
        state_ = FrameBufferState.WRITING;
      } else if (state_ == FrameBufferState.AWAITING_REGISTER_READ) {
        prepareRead();
      } else if (state_ == FrameBufferState.AWAITING_CLOSE) {
        close();
        selectionKey_.cancel();
      } else {
        LOGGER.error("changeSelectInterest was called, but state is invalid (" + state_ + ")");
      }
    }

也即判断当前状态,若为准备写,则注册client的写事件,若为读,注册读事件,并提前做好准备。

 

主要逻辑大概如上,现在让我们注意一下thrift中nonblcokingServer用到的一个特殊的transport==》TFramedTransport。它出现在AbstractNonblockingServerArgs的构造函数中:

public AbstractNonblockingServerArgs(TNonblockingServerTransport transport) {
      super(transport);
      transportFactory(new TFramedTransport.Factory());
    }

 在NonblockingServer被构建时传入,在frameBuffer的invoke方法中被使用:

     TTransport inTrans = getInputTransport();
      TProtocol inProt = inputProtocolFactory_.getProtocol(inTrans);
      TProtocol outProt = outputProtocolFactory_.getProtocol(getOutputTransport());

上面代码中的 inTrans和outProt其类型都为TFramedTransport。

它跟其它的transport的区别为“TFramedTransport is a buffered TTransport that ensures a fully read message every time by preceding messages with a 4-byte frame size.” 即每个数据包头4个byte,即一个int,指明该包(frame)内容的大小,每次读写都是以frame为单位读写的。

posted @ 2014-11-03 15:42  五色光  阅读(1336)  评论(0编辑  收藏  举报