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

看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为单位读写的。

libthrift0.9.0解析(五)之TNonblockingServer&THsHaServer的更多相关文章

  1. libthrift0.9.0解析(四)之TThreadPoolServer&ServerContext

    TThreadPoolServer直接继承自TServer,实现类serve和stop操作. 在serve中可以接受多个连接,每个连接单独开一个线程进行处理,在每个线程中,按顺序处理该线程所绑定连接的 ...

  2. libthrift0.9.0解析(三)之TProtocol&TTransport

       以上是transport和protocol的类图和结构图. transport封装了底层的传输通道,主要有read/write.open/close等基本的读写方法,而且都是对于二进制数据. p ...

  3. libthrift0.9.0解析(二)之TSimpleServer

    TSimpleServer简单实现Tserver,代码如下. /** * Simple singlethreaded server for testing. * */ public class TSi ...

  4. libthrift0.9.0解析(一)之TServer

    TServer 属性serverTransport 为TServerTransport类型,类图如下: 构造函数,简单根据args设置几个成员,大部分是工厂类: protected TServer(A ...

  5. C语言文件操作解析(五)之EOF解析(转载)

      C语言文件操作解析(五)之EOF解析 在C语言中,有个符号大家都应该很熟悉,那就是EOF(End of File),即文件结束符.但是很多时候对这个理解并不是很清楚,导致在写代码的时候经常出错,特 ...

  6. dubbo源码解析五 --- 集群容错架构设计与原理分析

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...

  7. Spring Security 解析(五) —— Spring Security Oauth2 开发

    Spring Security 解析(五) -- Spring Security Oauth2 开发   在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决 ...

  8. Celery 源码解析五: 远程控制管理

    今天要聊的话题可能被大家关注得不过,但是对于 Celery 来说确实很有用的功能,曾经我在工作中遇到这类情况,就是我们将所有的任务都放在同一个队列里面,然后有一天突然某个同学的代码写得不对,导致大量的 ...

  9. IIS6.0解析漏洞

    IIS6.0解析漏洞分两种 1.目录解析 以*.asp命名的文件夹里的文件都将会被当成ASP文件执行. 2.文件解析 *.asp;.jpg 像这种畸形文件名在“:”后面的直接被忽略,也就是说当成 *. ...

随机推荐

  1. 【面试】惠普IT电面

    1.英文自我介绍 一定要事先准备,逻辑要清晰 2.英文项目介绍

  2. Delphi控件的透明与不透明(要挨个解释一下原因),对InvalidateControl的关键理解

    procedure TForm1.Button3Click(Sender: TObject);begin if (csOpaque in ControlStyle) then ShowMessage( ...

  3. 6.ListView

    Repeater一般只用来展示数据,如果要增删改则用ListView更方便,使用向导(强类型数据)来使用ListView会自动生成很多模板,免去手写模板代码的麻烦,再进行手工调整即可. 首先设定数据源 ...

  4. oracle 表的管理(数据类型,表创建删除,数据CRUD 操作)

    表名和列的命名规则

  5. vim 的升级 安装 重装

    转载自http://blog.chinaunix.net/uid-22891521-id-2109310.html 由于一直以来在一个很old的linux版本下搞开发,里面的vim固然也是一个很old ...

  6. 关于Linux

    这是一个2B让我写的关于Linux的一点东西. 其实我对Linux一直都是持有一种很尊敬的态度,作为一个非商业性的操作系统,能够成长成这样简直是不可思议,有一种Dota在游戏界的感觉,很让人佩服.但是 ...

  7. JavaScript中的声明提升

    JavaScript中变量或者函数的声明会被提升(赋值语句不会被提升)到当前函数主体的顶部,不管这个声明是否出现在不可到达的地方. var test = 1; function f() { if(!t ...

  8. selenium grid 搭建

    hub端 Step1: 下载 selenium-server-standalone-x.xx.x.jar我下载的是:selenium-server-standalone-2.44.0.jar下载地址: ...

  9. 使用python进行接口测试

    最近在做接口测试,之前公司的方案是使用postman进行接口测试.但是伟大的墙导致我们只能用离线版postman..然后一个很长很长的接口列表,一个接一个的访问.我的天哪..所以萌生了一个想法,使用p ...

  10. struct--------构造函数对结构体初始化的影响

    struct--------构造函数对结构体初始化的影响. 没有构造函数时使用如下: struct ClassBook{  int number;  int age; }; int main() { ...