FrameBuffer
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