请求的处理是整个Tomcat的核心。深刻了解Tomcat的请求过程,对于咱们理解咱们的应用项目,对于咱们解决问题,对于咱们从此开发项目都有深远的影响java
若是看过Tomcat原理系列之二:由点到线,请求主干;必定对请求链具体走了哪些组件有了印象。咱们再进一步拆解请求链,将链上涉及的类一一道来。app
Connector组件做为Server的一部分,主要用于接收,解析http请求,并将请求封装成requset交给Container容器进行处理. Connector是如何接受请求的呢?框架
Connector使用ProtocolHandler(协议处理器)来处理请求的, 不一样的ProtocolHandler处理不一样模式的请求类型. 例如: Http11Protocol支持BIO类型的Socket来链接的, Http11NioProtocol支持NIO类型的NioSocket来链接的 ((基于Tomcat8)由于Tomcat高版本默认使用NIO模式,本文以NIO类型的处理来说) socket
经过Http11NioProtocol的构造方法. 咱们能够看出,主要包括两大部分: Endpoint:端点, 也就是socket的请求的入口. ConnectionHandler: Connection 处理器ide
public Http11NioProtocol() {
endpoint=new NioEndpoint();//端点
cHandler = new Http11ConnectionHandler(this);//connection处理器
((NioEndpoint) endpoint).setHandler(cHandler);
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
复制代码
NioEndpoint是整个Tomcat的请求的入口. NioEndponit类中有个几个内部类很是重要,也是请求的必经之地.咱们按照请求的走过的顺序一个个的解开看. oop
接收socket线程. Acceptor自己就是一个线程,run()方法里有while循环执行 serverSock.accept()接收线程. 此处有一个点要注意, 虽然是基于NIO的Endpoint可是这里仍是阻塞式接收socket链接的方法. 也就是说会阻塞到serverSock.accept();
(1)当获取到SocketChannel对象后, 调用setSocketOptions(socket)方法, 将SocketChannel封装到NioChannel对象中. (2)并调用Poller的register(Niochannel socket)方法,将NioChannel进一步封装到NioSocketWrapper对象中, (3)最后在将NioSocketWrapper对象封装到PollerEvent对象压到Poller的 events队列里中去.学习
这里是一个典型的生产-消费者模式Acceptor 是events queue的生产者, Poller是envets queue的消费者.this
(代码有删减,只把重要的提取出来)spa
@Override
public void run() {
while (running) {
try {
//接收线程(没有链接时阻塞在此处)
socket = serverSock.accept();
} catch (IOException ioe) {
}
// Successful accept, reset the error delay
errorDelay = 0;
// Configure the socket
if (running && !paused) {
// setSocketOptions()======= 对socket进一步处理
if (!setSocketOptions(socket)) {
closeSocket(socket);
}
} else {
closeSocket(socket);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("endpoint.accept.fail"), t);
}
}
state = AcceptorState.ENDED;
}
复制代码
主要用于以较少的资源轮询已链接套接字以保持链接,当数据可用时转给worker工做线程. Poller线程run方法,while循环消费events queue里的PollerEvent事件 . 经过 event()方不断取出PollerEvent对象,而后将PollerEvent对象中的NioSocketWrapper包装类以OP_READ事件注册到Poller的Selector选择器去.{此处咱们才真正看到NIO的影子} 在注册完OP_READ事件后. 紧接着执行**selector.selectedKeys()**方法将就绪的通道key返回,而后经过selectedKey访问就绪的通道. 取出NioSocketWrapper对象. 接着调用Poller的processKey()方法,根据sk是OPEN_READ事件或者OPEN_WRITE事件,调用NioEndpoint.processSocket()方法将NioSocketWrapper封装到SocketProcessor对象中, 并提交到NioEndpoint.executor线程池(Worker线程池)去执行.net
@Override
public void run() {
// Loop until destroy() is called
while (true) {
boolean hasEvents = false;
try {
if (!close) {
hasEvents = events();
if (wakeupCounter.getAndSet(-1) > 0) {
//if we are here, means we have other stuff to do
//do a non blocking select
keyCount = selector.selectNow();
} else {
keyCount = selector.select(selectorTimeout);
}
wakeupCounter.set(0);
}
if (close) {
events();
timeout(0, false);
try {
selector.close();
} catch (IOException ioe) {
log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
}
break;
}
} catch (Throwable x) {
ExceptionUtils.handleThrowable(x);
log.error("",x);
continue;
}
//either we timed out or we woke up, process events first
if ( keyCount == 0 ) hasEvents = (hasEvents | events());
Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (attachment == null) {
iterator.remove();
} else {
iterator.remove();
processKey(sk, attachment);
}
}//while
//process timeouts
timeout(keyCount,hasEvents);
}//while
getStopLatch().countDown();
}
复制代码
SocketProcessor处理socket链接的任务提交到worker线程池去执行
public boolean processSocket(SocketWrapperBase<S> socketWrapper, SocketEvent event, boolean dispatch) {
try {
if (socketWrapper == null) {
return false;
}
SocketProcessorBase<S> sc = processorCache.pop();
if (sc == null) {
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
Executor executor = getExecutor();
if (dispatch && executor != null) {
executor.execute(sc);//将socket处理任务提交到worker线程池
} else {
sc.run();
}
} catch (RejectedExecutionException ree) {
getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
return false;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This means we got an OOM or similar creating a thread, or that
// the pool and its queue are full
getLog().error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}
复制代码
NioEndpoint.SocketProcessor类做为一个任务, run()方法中,将socket的包装类NioEndpoint.NioSocketWrapper交给ProtocolHandler协议处理的ConnectionHandler进行处理
NioSocketWrapper 此时来到 ConnectionHandler这里.
ConnectionHandler.process()方法,首先获得一个Http11Processor 处理器, 而后会将NioSocketWrapper交给Http11Processor 的进行处理
在这里咱们回顾一个经典的问题:HTTP协议的组成部分,或者叫作HTTP协议的报文组成 1.请求行 2.请求头 3.空行 4.消息体
![]()
Http11Processor.service()方法中就是HTTP协议实现的地方. Http11Processor会建立一个Http11InputBuffer对象. Http11InputBuffer中的parseRequestLine、parseHeaders 和 parseHeader方法 会分别读取[请求行,请求头]
请求的读取去哪里了呢?? 对请求体的读取却不在这里. 而是将请求体的解析与读取延迟到Servlet中去了. 在调用getParameter,getParameterMap,getParameterNames,getParameterValues时会先调用parseParameters()方法解析请求参数. 这个放到之后再说.
Http11Processor的父类在初始化的时候,会建立Request对象,Response对象. 解析完请求行,请求头后. NioSocketWrapper对象此时变成了Request对象. 而后就开始下一程.
Http11Processor.service()方法处理后, 会调用CoyoteAdapter.service(request,repose) 对request,repose作一些预处理后,又开始了下一程connector.getService().getContainer().getPipeline().getFirst().invoke( request, response);
Request,Response 对象层层通过Engine,Host,Context,Wrapper的valve.
Valve做为一个个基础的阀门,扮演着业务实际执行者的角色.
在StandardWrapperValve.invoke方法中会根据配置的Filter过滤器建立ApplicationFilterChain过滤器链.
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
复制代码
而后执行过滤器链的doFilter方法
filterChain.doFilter(request.getRequest(),
response.getResponse());
复制代码
ApplicationFilterChain使用责任了模式,执行过滤器的doFilter方法. 当执行完最后一个Filter方法后. 调用
servlet.service(request, response);
复制代码
此时到了咱们常见的servlet了.终于到了咱们的业务代码了
socket ----->Connector接收socket链接封装成NioSocketWrapper对象 ----->ConnectionHandler取得HTTP协议内容,建立Requset,Reponse对象 ----->Container携带Requset,Reponse对象层级执行valve的invoke方法,到达最后一个StandardWrapperValve, 建立ApplicationFilterChain过滤器链, ----->ApplicationFilterChain过滤器链执行doFilter方法,执行完成后调用servlet.service()方法 ----->业务代码
我的理解有偏差,望指出. Tomcat中涉及的细节不少.须要慢慢品味. 可是请求的大致行动路线,就如上描述的那样. 但愿读者不断的去看源码挖掘更深的细节. 学习Tomact优秀的设计