[Tomcat源码系列]结构解析 3)请求处理控制结构
文章分类:Java编程
一、请求处理控制结构基础
与生命期结构类似,请求处理也是一个两层的结构
1.Valve:Valve是最小的处理单元,我们看看Valve的定义
下面是Valve的接口定义
- public interface Valve {
- public String getInfo();
- public Valve getNext();
- public void setNext(Valve valve);
- public void backgroundProcess();
- public void invoke(Request request, Response response)
- throws IOException, ServletException;
- public void event(Request request, Response response, CometEvent event)
- throws IOException, ServletException;
public interface Valve {
public String getInfo();
public Valve getNext();
public void setNext(Valve valve);
public void backgroundProcess();
public void invoke(Request request, Response response)
throws IOException, ServletException;
public void event(Request request, Response response, CometEvent event)
throws IOException, ServletException;
其中我们最需要关注的是invoke方法,请求处理处理方法,我们看看tomcat对这个方法的注释(如果扩展Valve,必须仔细阅读)
- Examine and/or modify the properties of the specified Request and Response.
- Examine the properties of the specified Request, completely generate the corresponding Response, and return control to the caller.
- Examine the properties of the specified Request and Response, wrap either or both of these objects to supplement their functionality, and pass them on.
- If the corresponding Response was not generated (and control was not returned, call the next Valve in the pipeline (if there is one) by executing context.invokeNext(). Examine, but not modify, the properties of the resulting Response (which was created by a subsequently invoked Valve or Container).
- Change request properties that have already been used to direct the flow of processing control for this request (for instance,trying to change the virtual host to which a Request should be sent from a pipeline attached to a Host or Context in the standard implementation).
- Create a completed Response AND pass this Request and Response on to the next Valve in the pipeline.
- Consume bytes from the input stream associated with the Request,unless it is completely generating the response, or wrapping the request before passing it on.
- Modify the HTTP headers included with the Response after the invokeNext() method has returned.
- Perform any actions on the output stream associated with the specified Response after the invokeNext()method has returned.
2.Pipeline:如上所说,“A series of Valves are generally associated with each other into a Pipeline”,我们Tomcat对Pipeline接口的说明
与其说是Pipeline(管道),其实此处似乎称为处理链更合适一点,中间每个处理节点(Valve)做一部分处理,然后由下一个处理节点继续处理,而通常最后一个处理节点必须处理请求并创建响应。如下是Pipeline接口的定义
- public interface Pipeline {
- public Valve getBasic();
- public void setBasic(Valve valve);
- public void addValve(Valve valve);
- public Valve[] getValves();
- public void removeValve(Valve valve);
- public Valve getFirst();
- }
public interface Pipeline {
public Valve getBasic();
public void setBasic(Valve valve);
public void addValve(Valve valve);
public Valve[] getValves();
public void removeValve(Valve valve);
public Valve getFirst();
}
二、请求处理过程解析
1.处理过程预览
Tomcat的主要处理组件Engine、Host、Context和Wrapper的实现都会实现Pipeline接口(实际是 ContainerBase实现了该接口)。在第一篇中,我们知道,实际对请求的处理是一个Adpater,Tomcat中Adapter的实现是 CoyoteAdapter,因此请求处理的入口是CoyoteAdapter的service方法,我们的请求处理过程就从CoyoteAdapter 开始
--组装好请求处理链
--StandardEngine. getPipeline().getFirst().invoke(request, response);
--XxxValve.invoke
--StandardEngineValve.invoke
2. StandardEngineValve.invoke
--Host. getPipeline().getFirst().invoke(request, response);
--YyyValve.invoke
--StandardHostValve.invoke
3. StandardHostValve.invoke
--Context. getPipeline().getFirst().invoke(request, response);
--ZzzValve.invoke
--StandardContextValve.invoke
4. StandardContextValve.invoke
--ServletRequestListener. requestInitialized
--Wrapper. getPipeline().getFirst().invoke(request, response);
--StandardWrapperValve.invoke
-- ServletRequestListener. requestDestroyed
5. StandardWrapperValve.invoke
--组装Filter+Servlet
--处理请求
2. 处理解析
1)我们从CoyoteAdapter.service为入口,看看Tomcat的整个请求处理过程
CoyoteAdapter.service
- public void service(org.apache.coyote.Request req,
- org.apache.coyote.Response res)
- throws Exception {
- Request request = (Request) req.getNote(ADAPTER_NOTES);
- Response response = (Response) res.getNote(ADAPTER_NOTES);
- if (request == null ) {
- //创建request、response对象
- ...略
- }
- try {
- // Parse and set Catalina and configuration specific
- // request parameters
- req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
- //组装请求处理链在此部分进行,后面详细解析
- if (postParseRequest(req, request, res, response)) {
- // Calling the container
- connector.getContainer().getPipeline().getFirst().invoke(request, response); //此处的Container是StandardEngine对象
- ...略
- } catch (IOException e) {
- ;
- } catch (Throwable t) {
- log.error(sm.getString("coyoteAdapter.service" ), t);
- } finally {
- ...略
- }
- }
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
//创建request、response对象
...略
}
try {
// Parse and set Catalina and configuration specific
// request parameters
req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
//组装请求处理链在此部分进行,后面详细解析
if (postParseRequest(req, request, res, response)) {
// Calling the container
connector.getContainer().getPipeline().getFirst().invoke(request, response); //此处的Container是StandardEngine对象
...略
} catch (IOException e) {
;
} catch (Throwable t) {
log.error(sm.getString("coyoteAdapter.service"), t);
} finally {
...略
}
}
2)默认的StandardEngine这个Pipeline会有StandardEngineValve这个处理单元。我们可以配置其他的处理单元到处理链中,譬如Tomcat就定义了如下的处理单元
the request and response data received and sent by Tomcat.
Documentation at: /docs/config/valve.html -->
<Valve className="org.apache.catalina.valves.RequestDumperValve"/>
我们看看StandardEngineValve.invoke
- public final void invoke(Request request, Response response)
- throws IOException, ServletException {
- // Select the Host to be used for this Request
- //请求是属于哪个Host的在CoyoteAdapter.postParseRequest已经准备好,后面重点会讲解这个过程
- Host host = request.getHost();
- if (host == null ) {
- response.sendError
- (HttpServletResponse.SC_BAD_REQUEST,
- sm.getString("standardEngine.noHost" ,
- request.getServerName()));
- return ;
- }
- // Ask this Host to process this request
- host.getPipeline().getFirst().invoke(request, response);
- }
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
//请求是属于哪个Host的在CoyoteAdapter.postParseRequest已经准备好,后面重点会讲解这个过程
Host host = request.getHost();
if (host == null) {
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
3)同样的,StandardHost这个Pipeline会有StandardHostValve这个处理单元。我们可以配置其他的处理单元到处理链中,譬如Tomcat就定义了如下的处理单元
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt" pattern="common" resolveHosts="false"/>
StandardHostValve如何处理请求跟StandardEngineValve类似,接下来请求进入到StandardContextValve.invoke
4) 同样的,StandardContext这个Pipeline会有StandardContextValve这个处理单元。我们可以配置其他的处理单元到处理链中,譬如Tomcat就定义了如下的处理单元
prefix="localhost_access_log." suffix=".txt"
pattern="common"/>
我们看看StandardContextValve是如何处理请求的
- public final void invoke(Request request, Response response)
- throws IOException, ServletException {
- // Disallow any direct access to resources under WEB-INF or META-INF
- MessageBytes requestPathMB = request.getRequestPathMB();
- if ((requestPathMB.startsWithIgnoreCase( "/META-INF/" , 0 ))
- || (requestPathMB.equalsIgnoreCase("/META-INF" ))
- || (requestPathMB.startsWithIgnoreCase("/WEB-INF/" , 0 ))
- || (requestPathMB.equalsIgnoreCase("/WEB-INF" ))) {
- String requestURI = request.getDecodedRequestURI();
- notFound(requestURI, response);
- return ;
- }
- // Wait if we are reloading
- while (context.getPaused()) {
- try {
- Thread.sleep(1000 );
- } catch (InterruptedException e) {
- ;
- }
- }
- // Select the Wrapper to be used for this Request
- Wrapper wrapper = request.getWrapper();
- if (wrapper == null ) {
- String requestURI = request.getDecodedRequestURI();
- notFound(requestURI, response);
- return ;
- }
- //ServletRequestListener. requestInitialized
- ...略
- wrapper.getPipeline().getFirst().invoke(request, response);
- //ServletRequestListener.requestDestroyed
- ...略
- }
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Disallow any direct access to resources under WEB-INF or META-INF
MessageBytes requestPathMB = request.getRequestPathMB();
if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/META-INF"))
|| (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
String requestURI = request.getDecodedRequestURI();
notFound(requestURI, response);
return;
}
// Wait if we are reloading
while (context.getPaused()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
;
}
}
// Select the Wrapper to be used for this Request
Wrapper wrapper = request.getWrapper();
if (wrapper == null) {
String requestURI = request.getDecodedRequestURI();
notFound(requestURI, response);
return;
}
//ServletRequestListener. requestInitialized
...略
wrapper.getPipeline().getFirst().invoke(request, response);
//ServletRequestListener.requestDestroyed
...略
}
5) 同样的,StandardWrapper这个Pipeline会有StandardWrapperValve这个处理单元,详细处理过程可以参阅一下org.apache.catalina.core.StandardWrapperValve.invoke代码
3.请求处理链组装
在如上代码中可以看到,实际请求进入到StandardEngineValve的时候,由哪个Host、哪个Context、哪个Wrapper参与处理 过程实际已经确定下来了,我们看看这个过程是如何处理的。Tomcat使用 org.apache.tomcat.util.http.mapper.Mapper来管理这种请求如何映射到具体Host、Context、 Wrapper。这个过程分为两个阶段
1)初始化阶段
在Engine、Host、Context的初始化阶段,会将子组件通过addChild方法加入到父组件中,我们以StandardContext.addChild为例看看如何处理
- public void addChild(Container child) {
- // Global JspServlet
- Wrapper oldJspServlet = null ;
- if (!(child instanceof Wrapper)) {
- throw new IllegalArgumentException
- (sm.getString("standardContext.notWrapper" ));
- }
- Wrapper wrapper = (Wrapper) child;
- boolean isJspServlet = "jsp" .equals(child.getName());
- // Allow webapp to override JspServlet inherited from global web.xml.
- if (isJspServlet) {
- oldJspServlet = (Wrapper) findChild("jsp" );
- if (oldJspServlet != null ) {
- removeChild(oldJspServlet);
- }
- }
- String jspFile = wrapper.getJspFile();
- if ((jspFile != null ) && !jspFile.startsWith( "/" )) {
- if (isServlet22()) {
- if (log.isDebugEnabled())
- log.debug(sm.getString("standardContext.wrapper.warning" ,
- jspFile));
- wrapper.setJspFile("/" + jspFile);
- } else {
- throw new IllegalArgumentException
- (sm.getString("standardContext.wrapper.error" , jspFile));
- }
- }
- super .addChild(child);
- if (isJspServlet && oldJspServlet != null ) {
- /*
- * The webapp-specific JspServlet inherits all the mappings
- * specified in the global web.xml, and may add additional ones.
- */
- String[] jspMappings = oldJspServlet.findMappings();
- for ( int i= 0 ; jspMappings!= null && i<jspMappings.length; i++) {
- addServletMapping(jspMappings[i], child.getName());
- }
- }
- }
public void addChild(Container child) {
// Global JspServlet
Wrapper oldJspServlet = null;
if (!(child instanceof Wrapper)) {
throw new IllegalArgumentException
(sm.getString("standardContext.notWrapper"));
}
Wrapper wrapper = (Wrapper) child;
boolean isJspServlet = "jsp".equals(child.getName());
// Allow webapp to override JspServlet inherited from global web.xml.
if (isJspServlet) {
oldJspServlet = (Wrapper) findChild("jsp");
if (oldJspServlet != null) {
removeChild(oldJspServlet);
}
}
String jspFile = wrapper.getJspFile();
if ((jspFile != null) && !jspFile.startsWith("/")) {
if (isServlet22()) {
if(log.isDebugEnabled())
log.debug(sm.getString("standardContext.wrapper.warning",
jspFile));
wrapper.setJspFile("/" + jspFile);
} else {
throw new IllegalArgumentException
(sm.getString("standardContext.wrapper.error", jspFile));
}
}
super.addChild(child);
if (isJspServlet && oldJspServlet != null) {
/*
* The webapp-specific JspServlet inherits all the mappings
* specified in the global web.xml, and may add additional ones.
*/
String[] jspMappings = oldJspServlet.findMappings();
for (int i=0; jspMappings!=null && i<jspMappings.length; i++) {
addServletMapping(jspMappings[i], child.getName());
}
}
}
在如上处理过程当中,我们关注的重点是addServletMapping,进一步进入addServletMapping,我们可以看到最终会将Wrapper对象告诉给Mapper对象,代码如下
- public void addServletMapping(String pattern, String name,
- boolean jspWildCard) {
- // Validate the proposed mapping
- if (findChild(name) == null )
- throw new IllegalArgumentException
- (sm.getString("standardContext.servletMap.name" , name));
- pattern = adjustURLPattern(RequestUtil.URLDecode(pattern));
- if (!validateURLPattern(pattern))
- throw new IllegalArgumentException
- (sm.getString("standardContext.servletMap.pattern" , pattern));
- // Add this mapping to our registered set
- synchronized (servletMappings) {
- String name2 = (String) servletMappings.get(pattern);
- if (name2 != null ) {
- // Don't allow more than one servlet on the same pattern
- Wrapper wrapper = (Wrapper) findChild(name2);
- wrapper.removeMapping(pattern);
- mapper.removeWrapper(pattern);
- }
- servletMappings.put(pattern, name);
- }
- Wrapper wrapper = (Wrapper) findChild(name);
- wrapper.addMapping(pattern);
- // Update context mapper
- mapper.addWrapper(pattern, wrapper, jspWildCard);
- fireContainerEvent("addServletMapping" , pattern);
- }
public void addServletMapping(String pattern, String name,
boolean jspWildCard) {
// Validate the proposed mapping
if (findChild(name) == null)
throw new IllegalArgumentException
(sm.getString("standardContext.servletMap.name", name));
pattern = adjustURLPattern(RequestUtil.URLDecode(pattern));
if (!validateURLPattern(pattern))
throw new IllegalArgumentException
(sm.getString("standardContext.servletMap.pattern", pattern));
// Add this mapping to our registered set
synchronized (servletMappings) {
String name2 = (String) servletMappings.get(pattern);
if (name2 != null) {
// Don't allow more than one servlet on the same pattern
Wrapper wrapper = (Wrapper) findChild(name2);
wrapper.removeMapping(pattern);
mapper.removeWrapper(pattern);
}
servletMappings.put(pattern, name);
}
Wrapper wrapper = (Wrapper) findChild(name);
wrapper.addMapping(pattern);
// Update context mapper
mapper.addWrapper(pattern, wrapper, jspWildCard);
fireContainerEvent("addServletMapping", pattern);
}
StandardEngine和StandardHost也会有类似的处理,这里不再重复,可以直接看这两个类的addChild实现
2)请求处理链组装阶段
在如上分析CoyoteAdpater.service的过程当中,我们知道,在进入StandardEngineValve.invoke之前,会先把 请求处理链先准备好,实际上有了Mapper这个对象及如上的基础,这个处理过程不会太过复杂。代码处理过程是 CoyoteAdpater.service-->CoyoteAdapter. postParseRequest-->Mapper.mapper,有兴趣可以直接看 org.apache.tomcat.util.http.mapper.Mapper.mapper方法
三、通过如上层层处理,最终请求到达我们实际的处理Servlet