thrift的non-blocking io server都是从AbstractNonblockingServer继承过来的,可以看http://my.oschina.net/chengxiaoyuan/blog/612916 中的类图,AbstractNonblockingServer中封装了一个FrameBuffer和AsyncFrameBuffer来实现读写操作和业务调用。
FrameBuffer中通过各种状态控制读写事件的有序进行,看各种状态描述:
/** * Possible states for the FrameBuffer state machine. */ private enum FrameBufferState { // in the midst of reading the frame size off the wire READING_FRAME_SIZE,//读消息头,表示消息体的长度,也是buffer的第一个状态 // reading the actual frame data now, but not all the way done yet READING_FRAME,//读消息体 // completely read the frame, so an invocation can now happen READ_FRAME_COMPLETE,//读完消息,下面可以执行操作 // waiting to get switched to listening for write events AWAITING_REGISTER_WRITE,//等待切换到监听写事件 // started writing response data, not fully complete yet WRITING,//写返回数据 // another thread wants this framebuffer to go back to reading AWAITING_REGISTER_READ,//等待注册读事件 // we want our transport and selection key invalidated in the selector // thread AWAITING_CLOSE//等待关闭 }
先读消息头4字节,计算出消息体的长度 ,状态 READING_FRAME_SIZE -> READING_FRAME
根据长度读消息体,读完之后执行invoke方法处理业务,状态 READING_FRAME -> READ_FRAME_COMPLETE
调用responseReady()做好写数据准备,切换状态,根据状态改变Selector的注册事件,状态 AWAITING_REGISTER_WRITE -> WRITING
收到读事件后会调用write方法,会把buffer中的数据返回, 状态 WRITING-> READING_FRAME_SIZE
public class FrameBuffer { private final Logger LOGGER = LoggerFactory.getLogger(getClass().getName()); // the actual transport hooked up to the client. protected final TNonblockingTransport trans_; // the SelectionKey that corresponds to our transport protected final SelectionKey selectionKey_; // the SelectThread that owns the registration of our transport protected final AbstractSelectThread selectThread_; // where in the process of reading/writing are we? protected FrameBufferState state_ = FrameBufferState.READING_FRAME_SIZE; // the ByteBuffer we'll be using to write and read, depending on the // state protected ByteBuffer buffer_; protected final TByteArrayOutputStream response_; // the frame that the TTransport should wrap. protected final TMemoryInputTransport frameTrans_; // the transport that should be used to connect to clients protected final TTransport inTrans_; protected final TTransport outTrans_; // the input protocol to use on frames protected final TProtocol inProt_; // the output protocol to use on frames protected final TProtocol outProt_; // context associated with this connection protected final ServerContext context_; public FrameBuffer(final TNonblockingTransport trans, final SelectionKey selectionKey, final AbstractSelectThread selectThread) { trans_ = trans; selectionKey_ = selectionKey; selectThread_ = selectThread; buffer_ = ByteBuffer.allocate(4);//预初始化4个字节,消息头4个字节用于存放消息体长度 frameTrans_ = new TMemoryInputTransport(); response_ = new TByteArrayOutputStream(); inTrans_ = inputTransportFactory_.getTransport(frameTrans_); outTrans_ = outputTransportFactory_.getTransport(new TIOStreamTransport(response_)); inProt_ = inputProtocolFactory_.getProtocol(inTrans_); outProt_ = outputProtocolFactory_.getProtocol(outTrans_); if (eventHandler_ != null) { context_ = eventHandler_.createContext(inProt_, outProt_); } else { context_ = null; } } /** * Give this FrameBuffer a chance to read. The selector loop should have * received a read event for this FrameBuffer. * * @return true if the connection should live on, false if it should be * closed */ public boolean read() { if (state_ == FrameBufferState.READING_FRAME_SIZE) {//状态为读消息头,也就是获取消息体的长度 // try to read the frame size completely if (!internalRead()) {//读数据到buffer中 return false; } // if the frame size has been read completely, then prepare to // read the // actual frame. if (buffer_.remaining() == 0) {//如果读完 // pull out the frame size as an integer. int frameSize = buffer_.getInt(0);//读取4个字节 if (frameSize <= 0) { LOGGER.error("Read an invalid frame size of " + frameSize + ". Are you using TFramedTransport on the client side?"); return false; } // if this frame will always be too large for this server, // log the // error and close the connection. if (frameSize > MAX_READ_BUFFER_BYTES) { LOGGER.error("Read a frame size of " + frameSize + ", which is bigger than the maximum allowable buffer size for ALL connections."); return false; } // if this frame will push us over the memory limit, then // return. // with luck, more memory will free up the next time around. if (readBufferBytesAllocated.get() + frameSize > MAX_READ_BUFFER_BYTES) { return true; } // increment the amount of memory allocated to read buffers readBufferBytesAllocated.addAndGet(frameSize + 4);//存放已经读取的字节数 // reallocate the readbuffer as a frame-sized buffer buffer_ = ByteBuffer.allocate(frameSize + 4); buffer_.putInt(frameSize); state_ = FrameBufferState.READING_FRAME;//状态切换为正在读 } else { // this skips the check of READING_FRAME state below, since // we can't // possibly go on to that state if there's data left to be // read at // this one. return true; } } // it is possible to fall through from the READING_FRAME_SIZE // section // to READING_FRAME if there's already some frame data available // once // READING_FRAME_SIZE is complete. if (state_ == FrameBufferState.READING_FRAME) { if (!internalRead()) {//读取数据到buffer中 return false; } // since we're already in the select loop here for sure, we can // just // modify our selection key directly. if (buffer_.remaining() == 0) {//读完 // get rid of the read select interests selectionKey_.interestOps(0);//不获取读事件了 state_ = FrameBufferState.READ_FRAME_COMPLETE;//状态改为读完 } return true; } // if we fall through to this point, then the state must be invalid. LOGGER.error("Read was called but state is invalid (" + state_ + ")"); return false; } /** * Give this FrameBuffer a chance to write its output to the final * client. */ public boolean write() { if (state_ == FrameBufferState.WRITING) {//如果是写状态 try { if (trans_.write(buffer_) < 0) {//写数据到buffer return false; } } catch (IOException e) { LOGGER.warn("Got an IOException during write!", e); return false; } // we're done writing. now we need to switch back to reading. if (buffer_.remaining() == 0) {//如果写满就切换到读 prepareRead(); } return true; } LOGGER.error("Write was called, but state is invalid (" + state_ + ")"); return false; } /** * 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_ + ")"); } } /** * Shut the connection down. */ public void close() { // if we're being closed due to an error, we might have allocated a // buffer that we need to subtract for our memory accounting. if (state_ == FrameBufferState.READING_FRAME || state_ == FrameBufferState.READ_FRAME_COMPLETE || state_ == FrameBufferState.AWAITING_CLOSE) { readBufferBytesAllocated.addAndGet(-buffer_.array().length); } trans_.close(); if (eventHandler_ != null) { eventHandler_.deleteContext(context_, inProt_, outProt_); } } /** * Check if this FrameBuffer has a full frame read. */ public boolean isFrameFullyRead() { return state_ == FrameBufferState.READ_FRAME_COMPLETE; } /** * 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. */ public void responseReady() { // the read buffer is definitely no longer in use, so we will // decrement // our read buffer count. we do this here as well as in close // because // we'd like to free this read memory up as quickly as possible for // other // clients. readBufferBytesAllocated.addAndGet(-buffer_.array().length); if (response_.len() == 0) { // go straight to reading again. this was probably an oneway // method state_ = FrameBufferState.AWAITING_REGISTER_READ; buffer_ = null; } else { buffer_ = ByteBuffer.wrap(response_.get(), 0, response_.len());//数据写到buffer中 // set state that we're waiting to be switched to write. we do // this // asynchronously through requestSelectInterestChange() because // there is // a possibility that we're not in the main thread, and thus // currently // blocked in select(). (this functionality is in place for the // sake of // the HsHa server.) state_ = FrameBufferState.AWAITING_REGISTER_WRITE;//切换状态,后面根据此状态修改Selector事件 } requestSelectInterestChange();//改变selector事件 } /** * Actually invoke the method signified by this FrameBuffer. */ public void invoke() {//rpc调用 frameTrans_.reset(buffer_.array()); response_.reset(); try { if (eventHandler_ != null) { eventHandler_.processContext(context_, inTrans_, outTrans_); } processorFactory_.getProcessor(inTrans_).process(inProt_, outProt_);//业务处理 responseReady();//准备返回数据 return; } catch (TException te) { LOGGER.warn("Exception while invoking!", te); } catch (Throwable t) { LOGGER.error("Unexpected throwable while invoking!", t); } // This will only be reached when there is a throwable. state_ = FrameBufferState.AWAITING_CLOSE; requestSelectInterestChange(); } /** * Perform a read into buffer. * * @return true if the read succeeded, false if there was an error or * the connection closed. */ private boolean internalRead() { try { if (trans_.read(buffer_) < 0) {//读到buffer里面 return false; } return true; } catch (IOException e) { LOGGER.warn("Got an IOException in internalRead!", e); return false; } } /** * We're done writing, so reset our interest ops and change state * accordingly. */ private void prepareRead() { // we can set our interest directly without using the queue because // we're in the select thread. selectionKey_.interestOps(SelectionKey.OP_READ); // get ready for another go-around buffer_ = ByteBuffer.allocate(4); state_ = FrameBufferState.READING_FRAME_SIZE; } /** * When this FrameBuffer needs to change its select interests and * execution might not be in its select thread, then this method will * make sure the interest change gets done when the select thread wakes * back up. When the current thread is this FrameBuffer's select thread, * then it just does the interest change immediately. */ protected void requestSelectInterestChange() { if (Thread.currentThread() == this.selectThread_) { changeSelectInterests(); } else { this.selectThread_.requestSelectInterestChange(this); } } } // FrameBuffer