tomcat请求处理分析(五) 请求到响应流

来源:互联网 发布:怎么找淘宝刷手的工作 编辑:程序博客网 时间:2024/06/16 18:57

1.1.1.1  请求到响应界面流

请求处理的过程主要是将所有的东西解析成流,转化成对应的http报文,所以在这里我先不关注servlet因为它最终也就是解析成流里面的数据

processKey里面最终执行的是processSocket,它是线从缓存中获取对应的线程池,没有的话就创建一个,然后进行执行

protected boolean processSocket(KeyAttachmentattachment,SocketStatus status, booleandispatch) {
   
try {
       
if (attachment==null) {
           
return false;
       
}
        SocketProcessor sc =
processorCache.pop();
        if
( sc == null) sc = new SocketProcessor(attachment,status);
        else
sc.reset(attachment,status);
       
Executor executor =getExecutor();
        if
(dispatch &&executor != null) {
            executor.execute(sc)
;
       
} else{
            sc.run()
;
       
}
    }
catch (RejectedExecutionExceptionree) {
       
log.warn(sm.getString("endpoint.executor.fail",attachment.getSocket()),ree);
        return false;
   
} catch(Throwablet) {
        ExceptionUtils.handleThrowable(t)
;
       
// This means we got anOOM or similar creating a thread, or that
        // the pool and its queue arefull
       
log.error(sm.getString("endpoint.process.fail"),t);
        return false;
   
}
   
return true;

}
    在上面描述的线程中,响应到页面主要是先构建对应的缓冲流,然后将缓冲流中的数据写入到sockt通道,这样就实现到了页面,具体操作逻辑如下:(自下向上执行)

   下面我将与流相关的几步,进行一下讲述:

process:,AbstractProtocol$AbstractConnectionHandler (org.apache.coyote)

 

if (processor ==null) {
    processor = createProcessor()
;

}

 

protected Http11ProcessorcreateProcessor() {
    Http11Processor processor =
new Http11Processor(
           
proto.getMaxHttpHeaderSize(),(JIoEndpoint)proto.endpoint,
           
proto.getMaxTrailerSize(),proto.getAllowedTrailerHeadersAsSet(),
           
proto.getMaxExtensionSize(),proto.getMaxSwallowSize());
   
proto.configureProcessor(processor);
   
// BIO specificconfiguration
   
processor.setDisableKeepAlivePercentage(proto.getDisableKeepAlivePercentage());
   
register(processor);
    return
processor;
}

 

public Http11Processor(intheaderBufferSize,JIoEndpointendpoint, intmaxTrailerSize,
       
Set<String>allowedTrailerHeaders, intmaxExtensionSize, intmaxSwallowSize){

   
super(endpoint);

   
inputBuffer =new InternalInputBuffer(request,headerBufferSize);
   
request.setInputBuffer(inputBuffer);

    
outputBuffer =new InternalOutputBuffer(response,headerBufferSize);
   
response.setOutputBuffer(outputBuffer);

   
initializeFilters(maxTrailerSize,allowedTrailerHeaders,maxExtensionSize,maxSwallowSize);

}

这里不难看出构建了的outputBuffer这InternalOutputBuffer实例并与response进行关联,所以后面通过response进行一些相关属性操作就可以直接到缓冲流

 

process:,AbstractHttp11Processor(org.apache.coyote.http11)

 

getOutputBuffer().init(socketWrapper, endpoint);

/**
 *
给当前实例 outputBufferresponse封装的对象
 *
 *
给其成员变量NioChannel socket以及pool进行赋值
 *
 * */
@Override
publicvoid init(SocketWrapper<NioChannel> socketWrapper,
       
AbstractEndpoint<NioChannel>endpoint) throws IOException {

   
socket =socketWrapper.getSocket();
   
pool =((NioEndpoint)endpoint).getSelectorPool();

}

这一步进行的操作主要是将outputBuffer这个实例关联对应的socket通道,为最后将缓冲流的数据放入到sockt做铺垫

 

public void close()
   
throws IOException{

   
if (closed) {
       
return;
   
}
   
if (suspended) {
       
return;
   
}

   
//将缓冲去的字符刷新给页面
   
if (cb.getLength()>0) {
       
cb.flushBuffer();
   
}

 
     。。。。。。

}

   最终是将cb给刷新到了然后将数据返回到页面,看一下cb是怎么来的,由下不难看出将OutputBuffer给注入其通道

 

public OutputBuffer(intsize) {

   
bb = new ByteChunk(size);
   
bb.setLimit(size);
   
bb.setByteOutputChannel(this);
   
cb = new CharChunk(size);
   
cb.setLimit(size);
   
cb.setOptimizedWrite(false);
   
cb.setCharOutputChannel(this);

}

   这样做最后怎么获取数据呢?由下面可以看出其一层一层不断的拆解最后还是到InternalOutputBuffer缓冲实例,所以解析的流数据最终还是经过这个进行处理

 

addToBB:,InternalNioOutputBuffer(org.apache.coyote.http11)

那最终它又是怎么到流中去,得看一下addToBB方法,由两步比较和核心,第一步就是将buf即InternalNioOutputBuffer实例中的数据拷贝到niochannel总去,第二步将niochannel通道中的数据写入到socket通道

private synchronized void addToBB(byte[] buf, intoffset, intlength)
       
throws IOException{
   
if (length ==0) return;
   
//首先尝试先将数据发送出去
   
boolean dataLeft = flushBuffer(isBlocking());
    //这里只有在缓冲区里面已经没有数据了才继续发送
   
while (!dataLeft&& length >0) {
       
//首先将要发送的数据copyniochanel的发送buffer里面去
     
  int thisTime =transfer(buf,offset,length,socket.getBufHandler().getWriteBuffer());
        //计算还剩下多少字节没有写到niochannelbuffer里面,其实这里也就当做将数据转移到了niochannelbuffer就算是写出去了
       
length = length -thisTime;
       
//这里用于调整偏移量
       
offset = offset +thisTime;
       
//调用writeToSocket方法将niochannelbuffer的里面的数据通过socket写出去
       
int written =writeToSocket(socket.getBufHandler().getWriteBuffer(),
                isBlocking(), true);
       //如果在tomcatresponse里面有writelistener的话,可以异步的写
       
if (written ==0) {
            dataLeft =
true;
       
} else{
            dataLeft =flushBuffer(isBlocking())
;
        
}
    }

    NioEndpoint.KeyAttachment ka =(NioEndpoint.KeyAttachment)
socket.getAttachment();
    if
(ka != null)ka.access();//prevent timeouts for just doing client writes

   
if (!isBlocking()&& length >0) {
       
//在非阻塞的发送中,如果实在发送不出去,需要保存在额外的buffer里面
       
addToBuffers(buf,offset, length);
   
}

}

 

下面在看一下具体怎么写到通道里面去

private synchronized int writeToSocket(ByteBufferbytebuffer, booleanblock, booleanflip) throwsIOException{
   
if ( flip ) {
        bytebuffer.flip()
;
       
flipped =true;
   
}
   
int written =0;
   
NioEndpoint.KeyAttachmentatt = (NioEndpoint.KeyAttachment)socket.getAttachment();
    if
( att == null) throw newIOException("Keymust be cancelled");
    long
writeTimeout =att.getWriteTimeout();
   
Selector selector = null;
    try
{
        selector =
pool.get();
   
} catch(IOException x ) {
   
}
   
try {
      
 written = pool.write(bytebuffer, socket,selector, writeTimeout, block);
        do {
           
if (socket.flush(true,selector,writeTimeout))break;
       
}while( true );
   
} finally{
       
if ( selector!=null)pool.put(selector);
   
}
   
if ( block ||bytebuffer.remaining()==0) {
       
bytebuffer.clear();
       
flipped =false;
   
}
   
return written;

}

 

   pool实例,即NioBlockingSelector,可以看出其有阻塞和非组合两种写入方式,但最后都是通过socket.write(buf)写入socket通道就返回到页面,至于为什么写入到socket通道就能响应到页面可以看一下基于NIO的httpserver实现,主要SocketChannelImpl这个类,这里又一个简易的httpserver的实现,参考链接:

http://www.cnblogs.com/a294098789/p/5676566.html

 

public int write(ByteBuffer buf,NioChannelsocket,Selector selector,
                 long
writeTimeout, booleanblock) throwsIOException{
   
if ( SHARED &&block ) {
       
return blockingSelector.write(buf,socket,writeTimeout);
   
}
    SelectionKey key =
null;
    int
written = 0;
    boolean
timedout = false;
    int
keycount = 1;//assume we canwrite
   
long time =System.currentTimeMillis();//start the timeout timer
   
try {
       
while ((!timedout) && buf.hasRemaining() ) {
           
int cnt =0;
            if
( keycount > 0) { //only write ifwe were registered for a write
               
cnt = socket.write(buf);//write thedata
               
if (cnt == -1)throw new EOFException();

               
written += cnt;
                if
(cnt > 0) {
                    time = System.currentTimeMillis()
;//reset ourtimeout timer
                   
continue; //wesuccessfully wrote, try again without a selector
               
}
               
if (cnt==0&&(!block)) break;//don't block
           
}
           
if ( selector!=null){
               
//register OP_WRITE to theselector
               
if (key==null) key =socket.getIOChannel().register(selector,SelectionKey.OP_WRITE);
                else
key.interestOps(SelectionKey.OP_WRITE);
                if
(writeTimeout==0) {
                    timedout =buf.hasRemaining()
;
               
} else if(writeTimeout<0) {
                    keycount =selector.select()
;
               
} else{
                    keycount =selector.select(writeTimeout)
;
               
}
            }
           
if (writeTimeout>0 && (selector ==null || keycount==0) ) timedout= (System.currentTimeMillis()-time)>=writeTimeout;
       
}//while
       
if ( timedout )thrownewSocketTimeoutException();
   
} finally{
       
if (key !=null) {
            key.cancel()
;
            if
(selector != null)selector.selectNow();//removes the key from this selector
       
}
    }
    
return written;

}

 

原创粉丝点击