Hadoop的RPC如何处理IO
Hadoop在RPC的Server端使用了NIO的方式来处理客户端的请求。
Server端没有线程池,Listener类会打开一个线程,该类拥有一个Selector,该Selector负责接收链接,处理链接的输入流。一旦一个客户端链接被接受,Listener会构造一个Connection对象。该Connection对象作为客户端SocketChannel的attachment,这样,后面的每一次处理客户端的SocketChannel,都会用到Connection。该Connection维护了客户端的Socket,以及要发送的响应列表(responseQueue)。Listener的doRead方法试图读取一个完整的方法请求,如果成功,会构造一个Call对象,该对象代表了一个方法请求的调用,维护了请求参数,和该请求所在的Connection。这个Call对象会被放到一个阻塞队列(callQueue),该队列是Server的一个实例变量。
请求会在一个单独的线程被处理,Handler类负责这项工作。Handler在一个while(running)循环里,不断调用callQueue的take方法,试图拿到一个方法调用的信息(Call),然后调用一个抽象的call方法处理这个调用。处理完成之后,调用setupResponse方法为该Call对象生成相应的字节流,该字节流是Call的一个属性,类型为ByteBuffer。
Responder是一个单独的线程来处理api调用的相应输出。该Responder维护了一个Selector类型的,名为writeSelector的实例变量。该Selector负责管理各客户端的输出处理。上面的描述指出,Handler负责处理call的请求处理,之后创建该call的输出流,并将该call放入到该call对应的Connection对象的responseQueue的尾端(调用addLast)。如果此时responseQueue只有一个call对象,调用Responder.processResponse(LinkedList responseQueue, boolean inHandler)方法,并传递参数inHandler为true。在Responder的processResponse方法中,首先会调用channelWrite方法将responseQueue里的第一个call对象的输出流写到SocketChannel中,如果没有响应字节流没有写完,会调用writeSelector.wakeup,接着调用channel.register(writeSelector, SelectionKey.OP_WRITE, call),使writeSelector监听该SocketChannel的OP_WRITE事件。
在Responder的while(running)循环中,会调用processResponse处理每一个Connection的responseQueue。如果该responseQueue里的call都处理完成,会调用key.interst(0)取消writeSelector对该SocketChannel的OP_WRITE事件监听。