heyeqingquan 2012-08-24
本次讲解一下Tomcat请求处理的流程,不当之处还请comment。
一.Tomcat总体结构
Tomcat采用模块化管理,下面是Tomcat的总体结构图:
从上图中可以看出Tomcat的核心是两个组件:Connector和Container。下面是一些概念的介绍。
①Server
一个server代表了整个catalinaservlet容器,在Tomcat里面的Server的用处是启动和监听服务端事件(诸如重启、关闭等命令)。
②Service
Service是由一个或多个Connector与一个Engine的组合。
③Connector
Connector将在某个指定的端口上监听客户的请求,把从socket传递过来的数据,封装成Request,传递给Engine来处理,并从Engine处获得响应并返回给客户。
Tomcat通常会用到两种Connector:
a)HttpConnector在端口8080处侦听来自客户browser的http请求。
b)AJPConnector在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求。
二、请求处理过程解析
1.Connector处理请求
Connector处理请求的流程大致如下:
Connector组件启动后,会侦听相关的端口的客户端请求。
(1)接受一个新的连接请求(org.apache.tomcat.util.net.TcpWorkerThread)
Java代码
voidrunIt(Object[]perThrData){
Sockets=null;
try{
s=endpoint.acceptSocket();//获取一个请求
}finally{
if(endpoint.isRunning()){
endpoint.tp.runIt(this);
//此处启动另一个TcpWorkerTread去接受其他请求,此线程处理已接受的请求
}
}
TcpConnectioncon=null;
con=(TcpConnection)perThrData[0];
con.setEndpoint(endpoint);
con.setSocket(s);endpoint.getConnectionHandler().processConnection(con,(Object[])perThrData[1]);
}
(2)新接收的请求被传到Http11ConnectionHandler中处理。(org.apache.coyote.http11.Http11Protocol.Http11ConnectionHandler)
Java代码
voidprocessConnection(TcpConnectionconnection,Object[]thData){
Http11Processorprocessor=null;
processor=(Http11Processor)thData[Http11Protocol.THREAD_DATA_PROCESSOR];
socket=connection.getSocket();
InputStreamin=socket.getInputStream();
OutputStreamout=socket.getOutputStream();
processor.setSocket(socket);
processor.process(in,out);
//processor是org.apache.coyote.http11.Http11Processor的一个实例
}
(3)在Http11Processor中处理http11协议相关的信息(org.apache.coyote.http11.Http11Processor)
Java代码
voidprocess(InputStreaminput,OutputStreamoutput)throwsIOException{
~~略~~
inputBuffer.setInputStream(input);
outputBuffer.setOutputStream(output);
inputBuffer.parseHeaders();
//http11协议头在此方法中被取出
adapter.service(request,response);
//adapter是org.apache.catalina.connector.CoyoteAdapter的一个实例
}
接下来的流程交由容器进行处理。
2.容器处理请求
容器交由Pipeline处理,这个Pipeline里面会放置一些vavle,请求沿着pipeline传递下去并且vavle对其进行相关的处理。比如说日志等,valve还可以自定义,具体需要查看server.xml配置文件。相关类图如下:
Tomcat的主要处理组件Engine、Host、Context和Wrapper的实现都会实现Pipeline接口,实际对请求的处理是一个Adpater,Tomcat中Adapter的实现是CoyoteAdapter,因此容器请求处理的入口是CoyoteAdapter的service方法。
1.CoyoteAdapter.service
--组装好请求处理链
--StandardEngine.getPipeline().getFirst().invoke(request,response);
--StandardEngineValve.invoke
2.StandardEngineValve.invoke
--Host.getPipeline().getFirst().invoke(request,response);
--StandardHostValve.invoke
3.StandardHostValve.invoke
--Context.getPipeline().getFirst().invoke(request,response);
--StandardContextValve.invoke
4.StandardContextValve.invoke
--ServletRequestListener.requestInitialized
--Wrapper.getPipeline().getFirst().invoke(request,response);
--StandardWrapperValve.invoke
--ServletRequestListener.requestDestroyed
5.StandardWrapperValve.invoke
--组装Filter+Servlet
--处理请求
(1)Connector传来的请求调用CoyoteAdapter.service()方法。(org.apache.catalina.connector.CoyoteAdapter)
Java代码
publicvoidservice(org.apache.coyote.Requestreq,
org.apache.coyote.Responseres)
throwsException{
~~略~~
if(request==null){
request=(Request)connector.createRequest();
request.setCoyoteRequest(req);
response=(Response)connector.createResponse();
response.setCoyoteResponse(res);
//创建request、response对象
~~略~~
}
try{
if(postParseRequest(req,request,res,response)){
connector.getContainer().getPipeline().getFirst().invoke(request,response);
//此处的Container是StandardEngine对象
~~略~~
}
}
(2)默认StandardEngine的Pipeline会有StandardEngineValve处理单元(参照StandardEngine构造函数)。(org.apache.catalina.core.StandardEngineValve)
Java代码
publicfinalvoidinvoke(Requestrequest,Responseresponse)
throwsIOException,ServletException{
//SelecttheHosttobeusedforthisRequest
Hosthost=request.getHost();
if(host==null){
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
//AskthisHosttoprocessthisrequest
host.getPipeline().getFirst().invoke(request,response);
}
(3)同样的,StandardHost的Pipeline会有StandardHostValve处理单元。StandardHostValve如何处理请求跟StandardEngineValve类似,接下来请求进入到StandardContextValve.invoke
(4)同样的,StandardContext的Pipeline会有StandardContextValve处理单元。
Java代码
publicfinalvoidinvoke(Requestrequest,Responseresponse)
throwsIOException,ServletException{
//DisallowanydirectaccesstoresourcesunderWEB-INForMETA-INFMessageBytesrequestPathMB=request.getRequestPathMB();
if((requestPathMB.startsWithIgnoreCase("/META-INF/",0))
||(requestPathMB.equalsIgnoreCase("/META-INF"))
||(requestPathMB.startsWithIgnoreCase("/WEB-INF/",0))
||(requestPathMB.equalsIgnoreCase("/WEB-INF"))){
StringrequestURI=request.getDecodedRequestURI();
notFound(requestURI,response);
return;
}
//Waitifwearereloading
while(context.getPaused()){
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
;
}
}
//SelecttheWrappertobeusedforthisRequest
Wrapperwrapper=request.getWrapper();
if(wrapper==null){
StringrequestURI=request.getDecodedRequestURI();
notFound(requestURI,response);
return;
}
//ServletRequestListener.requestInitialized
~~略~~
wrapper.getPipeline().getFirst().invoke(request,response);
//ServletRequestListener.requestDestroyed
~~略~~
}
(5)同样的,StandardWrapper这个Pipeline会有StandardWrapperValve这个处理单元。在invoke()方法调用Filter的同时,servlet.service()方法也将会被调用。
(org.apache.catalina.core.StandardWrapperValve)
Java代码
voidinvoke(Requestrequest,Responseresponse,ValveContextvalveContext)
throwsIOException,ServletException{
Servletservlet=null;
HttpServletRequesthreq=(HttpServletRequest)request.getRequest();
//org.apache.catalina.Request被封装成javax.servlet.http.HttpServletRequest.
HttpServletResponsehres=(HttpServletResponse)response.getResponse();
//org.apache.catalina.Response被封装成javax.servlet.http.HttpServletResponse.
servlet=wrapper.allocate();//装载servlet
if((servlet!=null)&&(filterChain!=null)){
filterChain.doFilter(hreq,hres);//调用此servlet的filterchain
}
(6)调用servlet的filterchain处理request和response
(org.apache.catalina.core.ApplicationFilterChain)
Java代码
voiddoFilter(ServletRequestrequest,ServletResponseresponse)throws
IOException,ServletException{
~~略~~
internalDoFilter(request,response);
~~略~~
}
(7)调用internalDoFilter()处理请求。(org.apache.catalina.core.ApplicationFilterChain)
Java代码
voidinternalDoFilter(ServletRequestrequest,ServletResponseresponse)throws
IOException,ServletException{
//此处省略filter处理的代码,filter被一个一个调用。
//如果http请求的是一个jsp页面,下面的servlet会是org.apache.jasper.servlet.JspServlet类的一个实例
//若是html页面,下面的servlet会是org.apache.catalina.servlets.DefaultServlet类的一个实例
if((requestinstanceofHttpServletRequest)&&
(responseinstanceofHttpServletResponse)){
servlet.service((HttpServletRequest)request,(HttpServletResponse)response);
servlet.service(request,response);
}else{
servlet.service(request,response);
}
}
至此,servlet.service()方法被调用。
本文实例讲述了Linux下源码包安装Swoole及基本使用操作。分享给大家供大家参考,具体如下:。//创建Server对象,监听 127.0.0.1:9502端口,类型为SWOOLE_SOCK_UDP