tomcat请求处理分析(五) 请求到响应流
2017-08-19 00:02
399 查看
1.1.1.1 请求到响应界面流
请求处理的过程主要是将所有的东西解析成流,转化成对应的http报文,所以在这里我先不关注servlet因为它最终也就是解析成流里面的数据processKey里面最终执行的是processSocket,它是线从缓存中获取对应的线程池,没有的话就创建一个,然后进行执行
protected boolean processSocket(KeyAttachmentattachment,
SocketStatus status, boolean
dispatch) {
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 Http11Processor
createProcessor() {
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(int
headerBufferSize,
JIoEndpointendpoint, int
maxTrailerSize,
Set<String>allowedTrailerHeaders, int
maxExtensionSize, int
maxSwallowSize){
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);
/**
* 给当前实例 outputBuffer即response封装的对象
*
* 给其成员变量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(int
size) {
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, int
offset, int
length)
throws IOException{
if (length ==
0) return;
//首先尝试先将数据发送出去
boolean dataLeft = flushBuffer(isBlocking());
//这里只有在缓冲区里面已经没有数据了才继续发送
while (!dataLeft&& length >
0) {
//首先将要发送的数据copy到niochanel的发送buffer里面去
int thisTime =transfer(buf,offset,length,socket.getBufHandler().getWriteBuffer());
//计算还剩下多少字节没有写到niochannel的buffer里面,其实这里也就当做将数据转移到了niochannel的buffer就算是写出去了
length = length -thisTime;
//这里用于调整偏移量
offset = offset +thisTime;
//调用writeToSocket方法将niochannel的buffer的里面的数据通过socket写出去
int written =writeToSocket(socket.getBufHandler().getWriteBuffer(),
isBlocking(), true);
//如果在tomcat的response里面有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, boolean
block, boolean
flip) throws
IOException{
if ( flip ) {
bytebuffer.flip();
flipped =
true;
}
int written =
0;
NioEndpoint.KeyAttachmentatt = (NioEndpoint.KeyAttachment)socket.getAttachment();
if ( att == null
) throw new
IOException("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, boolean
block) throws
IOException{
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 )thrownew
SocketTimeoutException();
} finally
{
if (key !=
null) {
key.cancel();
if (selector != null)selector.selectNow();//removes the key from this selector
}
}
return written;
}
相关文章推荐
- Tomcat 启动与处理请求分析(二)
- tomcat6源码分析三(请求处理过程)
- 请求与响应原理图及Tomcat对request的处理过程
- tomcat请求处理分析(一) 启动container实例
- tomcat请求处理分析(二) 启动mapperListener
- Ril分析三——客户端请求和响应处理与modem交互
- SpringMVC4.x源码分析(七):使用XStream处理xml请求和响应消息实战
- Tomcat 启动与处理请求分析(一)
- tomcat6源码分析三(请求处理过程)
- tomcat请求处理分析(三) 绑定本地端口监听请求
- tomcat请求处理分析(四) 监听请求轮询处理
- tomcat4 请求的处理——初步分析
- nginx记录分析网站响应慢的请求(ngx_http_log_request_speed)
- Tomcat请求处理(二) -- 请求处理框架
- 第五课 nodejs 路由实现并处理请求作出响应
- Tomcat处理HTTP请求源码分析(上)
- Spring MVC源码分析(续)——请求处理
- Tomcat处理一个http请求的过程
- intellij使用servlet发送和处理请求, 并用tomcat布置到服务器上
- Ajax响应中文乱码 [SpringMVC使用@ResponseBody处理Ajax请求]