- 浏览: 276185 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
highphd:
海量用户如何处理啊?缓存服务器?大数据?
面向海量服务的设计原则和策略总结 -
AKka:
看了这篇博文更感觉到自己要学的东西更多了。同时感谢博主的辛勤写 ...
[Java性能剖析]JVM Management API -
sswh:
非常不错,感谢分享!!
[Java性能剖析]Sun JVM Attach API -
muyexi:
请问在Android开发中的什么场景下,会用到ObjectWe ...
[字节码系列]ObjectWeb ASM构建Method Monitor -
zoutuo:
前辈可否告知其中的“吞吐量”指的是什么?谢谢!
[Java性能剖析]Sun JVM内存管理和垃圾回收
一、请求处理控制结构基础
与生命期结构类似,请求处理也是一个两层的结构
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;
其中我们最需要关注的是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(); }
二、请求处理过程解析
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 { ...略 } }
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); }
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 ...略 }
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()); } } }
在如上处理过程当中,我们关注的重点是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); }
StandardEngine和StandardHost也会有类似的处理,这里不再重复,可以直接看这两个类的addChild实现
2)请求处理链组装阶段
在如上分析CoyoteAdpater.service的过程当中,我们知道,在进入StandardEngineValve.invoke之前,会先把请求处理链先准备好,实际上有了Mapper这个对象及如上的基础,这个处理过程不会太过复杂。代码处理过程是CoyoteAdpater.service-->CoyoteAdapter. postParseRequest-->Mapper.mapper,有兴趣可以直接看org.apache.tomcat.util.http.mapper.Mapper.mapper方法
三、通过如上层层处理,最终请求到达我们实际的处理Servlet
发表评论
-
Tomcat Context reloadabled 与 OutOfMemory(PermSpace)
2010-04-17 13:24 6851我们知道,Sun J ... -
[Tomcat源码系列] 扩展
2010-04-06 22:09 2736一、 Realm/ HTTP认证 1)Realm R ... -
[Tomcat源码系列] Tomcat 类加载器结构
2010-04-02 22:33 5278一、从类加载器(ClassLoad ... -
[Tomcat源码系列]结构解析 2)生命期控制结构
2010-03-28 07:37 2045一、生命期控制结构基础 Tomcat的生命期控制是一个两层的 ... -
[Tomcat源码系列]结构解析 1)总体结构预览
2010-03-27 08:14 2674一、从范例开始 在开始分析之前,我们先使用Tomca ... -
[Tomcat源码系列] Tomcat Connector
2010-03-20 07:15 8162Connector是Tomcat ...
相关推荐
### tomcat源码解析 #### 简介与概览 Tomcat作为一款开源的Servlet容器,被广泛应用于Java Web应用的开发与部署环境中。它不仅支持Servlet API,还支持JSP规范,使得开发者能够轻松地构建动态网页。本文旨在深入...
在Tomcat源码中,Catalina的脚本解析是了解其启动流程的关键。首先,Tomcat会解析conf/server.xml文件,该文件中定义了所有容器组件的配置。脚本解析的过程涉及到几个关键类,如Digester,它负责将XML文件中的配置...
1. 初始化:Tomcat启动时,会读取配置文件server.xml,解析配置信息,构建出服务器的结构。 2. 加载Web应用:根据context.xml配置加载Web应用,创建对应的Context对象。 3. 初始化Servlet:调用Servlet的init()方法...
《深入剖析Tomcat3:源码解析与运行机制》 Tomcat3作为Apache Tomcat的早期版本,虽然相比后来的6、7、8等版本在功能上显得较为简单,但正是这种简洁,使得它成为初学者研究Web服务器和Java Servlet容器理想的起点...
《深入剖析Tomcat源码:基于6.0版本》 Tomcat是一款开源的、轻量级的Java Servlet容器,它负责解析HTTP请求并调用Java应用程序。Tomcat 6.0是其历史上的一个重要版本,提供了许多关键特性和改进。通过深入研究其...
【Tomcat源码详解】 Tomcat,作为Apache软件基金会的项目之一,是广泛使用的Java Servlet容器,它实现了Java EE的Web应用服务器规范。Tomcat6.0版本是历史上非常流行的一个版本,对于开发者来说,深入理解其源码有...
Coyote处理与客户端的HTTP连接,解析请求并生成响应。它包括两个主要部分:Connector(处理连接)和ProtocolHandler(处理特定的网络协议,如HTTP/1.1)。 5. **Apr库** Apache Portable Runtime(Apr)提供了...
1. **目录结构**:Tomcat源码的根目录包含了许多子目录,如`bin`(启动脚本)、`conf`(配置文件)、`webapps`(默认应用程序)、`work`(工作目录)等。了解这些目录的作用对于管理和调试Tomcat非常重要。 2. **...
2. **解析请求**:Coyote将网络数据解析为`org.apache.coyote.Request`和`org.apache.coyote.Response`对象。 3. **请求分发**:请求通过`org.apache.catalina.connector.Request`类进行进一步处理,找到对应的...
在深入探讨Tomcat源码之前,我们先了解一下Tomcat是什么。Tomcat是一款开源的Java Servlet容器,由Apache软件基金会开发,它实现了Java EE中的Web应用服务器部分,特别是Servlet和JavaServer Pages (JSP)规范。《How...
当一个HTTP请求到达时,Coyote解析请求并将其传递给Catalina。Catalina根据请求的URL定位到对应的Context,进一步找到Servlet实例,执行Servlet的service()方法。如果涉及到JSP,Jasper会介入将JSP编译成Servlet并...
深入学习Tomcat源码,有助于我们掌握以下关键知识点: 1. **Servlet生命周期**:理解Servlet的初始化、服务、销毁过程,以及Tomcat如何管理和调度Servlet实例。 2. **容器结构**:Tomcat的容器模型包括Engine...
在开始学习Tomcat源码之前,首先需要了解一些基本概念。Java Servlet是Java平台上的一个标准接口,用于处理HTTP请求。而JSP则是用于创建动态网页的Java技术,它将业务逻辑和页面展示分离。Tomcat作为Servlet容器,...
1. **Tomcat架构**:了解Tomcat的基本结构,包括Catalina、Coyote、Jasper等核心组件,以及它们在请求处理中的角色和交互方式。 2. **Servlet与JSP解析**:学习JSP如何被编译成Servlet,以及Servlet生命周期管理,...
用户只需导入必要的jar包,即可使用Ant工具对Tomcat源码进行编译,这为开发者提供了极大的便利。 在探索Tomcat6.0.35源码时,你可以关注以下几个关键知识点: 1. **Servlet容器**:Tomcat作为Servlet容器,负责...
《深入剖析Tomcat 8.0底层源码》 Tomcat作为一款广泛应用的开源Java Servlet容器,其8.0版本的源码对于开发者来说是一份宝贵的教育资源。通过深入学习Tomcat 8.0的源码,我们可以理解Web服务器的工作原理,提升对...
1. Catalina:这是Tomcat的核心,负责管理Servlet容器,解析并执行请求。Catalina的启动过程涉及Server、Service、Engine、Host和Context等层次结构,这些元素在web.xml配置文件中定义。 2. Jasper:Jasper是Tomcat...
深入Tomcat源码,我们可以学习到以下关键知识点: 1. **Web应用部署**:Tomcat如何解析`WEB-INF/web.xml`配置文件,加载Servlet和Filter,以及如何根据`META-INF/context.xml`设置上下文参数。 2. **生命周期管理*...