`

深入剖析tomcat之servlet容器

 
阅读更多

其实我们开发中经常用tomcat应用服务器,tomcat就一个servlet容器,能够运行基于serlvlet的应用程序并响应相应的http请求,开发时间长了,还是想想具体知道它是怎么运行的,尤其是servlet容器的机理,所以有幸拜读了外国人的《深入剖析tomcat》,感觉挺不错的,可以在此点击免费下载电子书,建议大家有时间读读,在读的过程中边读边翻阅着tomcat的源码,更有助于你理解它的各个机制,此处有tomcat 7的源码,点击可免费下载

本人目前时间和组织语言能力及功力有限,心有余而力不足,网上看到有好心人(randyjiawenjie)做过记录,大概就是把书中重要的东西摘录的了一下,本人就就特意摘录以下了。再就是为了更有助理解,大家可以参考Tomcat 容器与servlet的交互原理,简单介绍了serlvet的原理和生命周期,还有学习tomcat源码(2) 实现servlet容器功能,这篇文章主要还是电子书的代码及解释,升华的文章Tomcat 系统架构与设计模式,第 2 部分: 设计模式分析

主要是《深入剖析tomcat》的第五章和第十一章。个人觉得如下3点是关键:

1. pipeline相关概念及其执行valve的顺序;

2.standardwrapper的接受http请求时候的调用序列;

3.standardwrapper基础阀加载servlet的过程(涉及到STM);

顺便问一句,应该每一个servlet程序员都知道filter。但是你知道Filter在tomcat的哪一个地方出现的吗?答案是standardwrapper基础阀会创建Filter链,并调用doFilter()方法

servlet容器的作用

管理servlet资源,并为web客户端填充response对象。

不同级别的容器

Engine:表示整个Catalina的servlet引擎、

Host:表示一个拥有数个上下文的虚拟主机

Context:表示一个Web应用,一个context包含一个或多个wrapper

Wrapper:表示一个独立的servlet

容器都实现了Container接口,继承了ContainerBase抽象类。

管道任务

3个概念:pipeline、valve、valveContext

pipeline包含servlet容器将要调用的任务集合,定义如下:

  1. publicinterfacePipeline{
  2. publicValvegetBasic();
  3. publicvoidsetBasic(Valvevalve);
  4. publicvoidaddValve(Valvevalve);
  5. publicValve[]getValves();
  6. publicvoidinvoke(Requestrequest,Responseresponse)throwsIOException,ServletException;
  7. publicvoidremoveValve(Valvevalve);
  8. }


valve表示一个具体的执行任务,定义如下:

  1. publicinterfaceValve{
  2. publicStringgetInfo();
  3. publicvoidinvoke(Requestrequest,Responseresponse,ValveContextcontext)throwsIOException,ServletException;
  4. }


valveContext按照字面意思就是阀门的上下文,用于遍历valve

  1. publicinterfaceValveContex{
  2. publicStringgetInfo();
  3. publicvoidinvokeNext(Requestrequest,Responseresponse)throwsIOException,ServletException;
  4. }


valveContext的invokeNext()方法的实现:

  1. publicfinalvoidinvokeNext(Requestrequest,Responseresponse)throwsIOException,ServletException{
  2. intsubscript=stage;stage=stage+1;
  3. if(subscript<valves.length)
  4. {valves[subscript].invoke(request,response,this);}
  5. elseif((subscript==valves.length)&&(basic!=null))
  6. {
  7. basic.invoke(request,response,this);
  8. }
  9. else
  10. {
  11. thrownewServletException(sm.getString("standardPipeline.noValve"));
  12. }
  13. }


一个阀门的invoke方法可以如下实现:

  1. publicvoidinvoke(Requestrequest,Responseresponse,ValveContextvalveContext)throwsIOException,ServletException{
  2. //Passtherequestandresponseontothenextvalveinourpipeline
  3. valveContext.invokeNext(request,response);
  4. //nowperformwhatthisvalveissupposedtodo...
  5. }


如果pipeline由valve1、valve2、valve3组成,调用valve1. Invoke()会发生什么?执行顺序是什么样的?假设valveN(N=1,2,3)的invoke()方法实现如下:

  1. valveContext.invokeNext(request,response);
  2. System.out.println(“valve1invoke!”);

如果注意到了invokeNext()的实现,这层调用类似与下面的图:

类似与递归调用,输出为

  1. “valve3invoke!”
  2. “valve2invoke!”
  3. “valve1invoke!”

顺序恰好于添加的顺序相反。所以这个也可以说明,为什么基础阀看起来是放在最后的,但确实要做装载servlet这样的操作。其实,放在最后的阀门总是第一个被调用。书中的依次添加HeaderLoggerValve、ClientIPLoggerValve依次添加,那么调用顺序为ClientIPLoggerValve、HeaderLoggerValve。注意到书中示例程序的运行结果和添加阀门的顺序。不过话说回来,如果valve的invoke()方法实现为:

  1. publicvoidinvoke(Requestrequest,Responseresponse,ValveContextvalveContext)throwsIOException,ServletException{
  2. //nowperformwhatthisvalveissupposedtodo...
  3. valveContext.invokeNext(request,response);//Passtherequestandresponseontothenextvalveinourpipeline
  4. }

那么阀门调用的顺序就会和阀门添加的顺序一致(个人感觉这样好理解)

Wrapper接口

Wrapper表示一个独立servlet定义的容器,wrapper继承了Container接口,并且添加了几个方法。包装器的实现类负责管理其下层servlet的生命周期。

包装器接口中重要方法有allocate和load方法。allocate方法负责定位该包装器表示的servlet的实例。Allocate方法必须考虑一个servlet,是否实现了avax.servlet.SingleThreadModel接口。

Wrapper调用序列:

(1)连接器调用Wrapper的invoke()方法;

(2)Wrapper调用其pipeline的invoke()方法;

(3)Pipeline调用valveContext.invokeNext()方法;

基础阀的invoke实现示例如下:

  1. publicvoidinvoke(Requestrequest,Responseresponse,ValveContextvalveContext)throwsIOException,ServletException{
  2. SimpleWrapperwrapper=(SimpleWrapper)getContainer();
  3. ServletRequestsreq=request.getRequest();
  4. ServletResponsesres=response.getResponse();
  5. Servletservlet=null;
  6. HttpServletRequesthreq=null;
  7. if(sreqinstanceofHttpServletRequest)
  8. hreq=(HttpServletRequest)sreq;
  9. HttpServletResponsehres=null;
  10. if(sresinstanceofHttpServletResponse)hres=(HttpServletResponse)sres;
  11. //Allocateaservletinstancetoprocessthisrequest
  12. try{
  13. servlet=wrapper.allocate();
  14. if(hres!=null&&hreq!=null)
  15. {
  16. servlet.service(hreq,hres);
  17. }
  18. else
  19. {
  20. servlet.service(sreq,sres);
  21. }
  22. }
  23. catch(ServletExceptione){
  24. }
  25. }


Context应用程序

Context包含多个wrapper,至于用哪一个wrapper,是由映射器mapper决定的。Mapper定义如下:

  1. publicinterfaceMapper{
  2. publicContainergetContainer();
  3. publicvoidsetContainer(Containercontainer);
  4. publicStringgetProtocol();
  5. publicvoidsetProtocol(Stringprotocol);
  6. publicContainermap(Requestrequest,booleanupdate);
  7. }

map方法返回一个子容器(wrapper)负责来处理请求。map方法有两个参数,一个request对象和一个boolean值。实现将忽略第二个参数。这个方法是从请求对象中获取context路径和使用context的findServletMapping方法获取相关的路径名。如果一个路径名被找到,它使用context的findChild方法获取一个Wrapper的实例。一个mapper的实现:

  1. publicContainermap(Requestrequest,booleanupdate){
  2. //Identifythecontext-relativeURItobemapped
  3. StringcontextPath=((HttpServletRequest)request.getRequest()).getContextPath();
  4. StringrequestURI=((HttpRequest)request).getDecodedRequestURI();
  5. StringrelativeURI=requestURI.substring(contextPath.length());
  6. //ApplythestandardrequestURImappingrulesfrom
  7. //thespecification
  8. Wrapperwrapper=null;
  9. StringservletPath=relativeURI;
  10. StringpathInfo=null;
  11. Stringname=context.findServletMapping(relativeURI);
  12. if(name!=null)
  13. wrapper=(Wrapper)context.findChild(name);
  14. return(wrapper);
  1. }

容器包含一条管道,容器的invoke方法会调用pipeline的invoke方法。

1. pipeline的invoke方法会调用添加到容器中的阀门的invoke方法,然后调用基本阀门的invoke方法。

2.在一个wrapper中,基础阀负责加载相关的servlet类并对请求作出相应。

3. 在一个包含子容器的Context中,基础阀使用mapper来查找负责处理请求的子容器。如果一个子容器被找到,子容器的invoke方法会被调用,然后返回步骤1。

Context的一个基础阀示例如下:注意最后一句话:wrapper.invoke(request,response);

  1. publicvoidinvoke(Requestrequest,Responseresponse,ValveContextvalveContext)
  2. throwsIOException,ServletException{
  3. //Validatetherequestandresponseobjecttypes
  4. if(!(request.getRequest()instanceofHttpServletRequest)||
  5. !(response.getResponse()instanceofHttpServletResponse)){
  6. return;
  7. }
  8. //DisallowanydirectaccesstoresourcesunderWEB-INForMETA-INF
  9. HttpServletRequesthreq=(HttpServletRequest)request.getRequest();
  10. StringcontextPath=hreq.getContextPath();
  11. StringrequestURI=((HttpRequest)request).getDecodedRequestURI();
  12. StringrelativeURI=requestURI.substring(contextPath.length()).toUpperCase();
  13. Contextcontext=(Context)getContainer();
  14. //SelecttheWrappertobeusedforthisRequest
  15. Wrapperwrapper=null;
  16. try{
  17. wrapper=(Wrapper)context.map(request,true);
  18. }catch(IllegalArgumentExceptione){
  19. badRequest(requestURI,(HttpServletResponse)
  20. response.getResponse());
  21. return;
  22. }
  23. if(wrapper==null){
  24. notFound(requestURI,(HttpServletResponse)response.getResponse());
  25. return;
  26. }
  27. //AskthisWrappertoprocessthisRequest
  28. response.setContext(context);
  29. wrapper.invoke(request,response);
  30. }

standardwrapper
方法调用序列Sequence of Methods Invocation
对于每一个连接,连接器都会调用关联容器的invoke方法。接下来容器调用它的所有子容器的invoke方法。如果一个连接器跟一个StadardContext实例相关联,那么连接器会调用StandardContext实例的invoke方法,该方法会调用所有它的子容器的invoke方法。

具体过程
1. 连接器创建request和response对象;
2.连接器调用StandardContext的invoke方法;
3.StandardContext的invoke方法必须调用该管道对象的invoke方法。StandardContext管道对象的基础阀是StandardContextValve类的实例。因此,StandardContext管道对象会调用StandardContextValve的invoke方法。
4.StandardContextValve的invoke方法获取相应的wrapper,处理http请求,调用wrapper的invoke方法
5.StandardWrapper是wrapper的标准实现,StandardWrapper对象的invoke方法调用pipeline的invoke方法。
6,StandardWrapper流水线的基本阀门时StandardWrapperValve。因此StandardWrapperValve的invoke方法会被调用。StandardWrapperValve的invoke方法会调用包装器的allocate方法获得一个servlet的实例。
7,当一个servlet需要被加载的时候,方法allocate调用方法load来加载一个servlet
8,方法load会调用servlet的init方法

我们主要关注的是一个servlet被调用的时候发生的细节。因此我们需要看StandardWrapper和StandarWrapperValve类

javax.servlet.SingleThreadModel
一个servlet可以实现javax.servlet.SingleThreadModel接口,实现此接口的一个servlet通俗称为SingleThreadModel(STM)的程序组件。
根据Servlet规范,实现此接口的目的是保证servlet一次只能有一个请求。

StandardWrapper实现
一个StandardWrapper对象的主要职责是:加载它表示的servlet,并实例化。该StandardWrapper不会调用servlet的service方法这个任务留给StandardWrapperValve对象,在StandardWrapper实例的基本阀门管道。StandardWrapperValve对象通过调用StandardWrapper的allocate方法获得Servlet实例。在获得Servlet实例之后的StandardWrapperValve调用servlet的service方法。

在servlet第一次被请求的时候,StandardWrapper加载servlet类。它是动态的加载servlet,所以需要知道servlet类的完全限定名称。通过StandardWrapper类的setServletClass方法将servlet的类名传递给StandardWrapper。必须考虑一个servlet是否实现了SingleThreadModel接口。 如果一个servlet没有实现SingleThreadModel接口,StandardWrapper加载该servlet一次,对于以后的请求返回相同的实例即可。

对于一个STM servlet,情况就有所不同了。StandardWrapper必须保证不能同时有两个线程提交STM servlet的service方法。

  1. Servletinstance=<getaninstanceoftheservlet>;
  2. if((servletimplementingSingleThreadModel>){
  3. synchronized(instance){
  4. instance.service(request,response);
  5. else{
  6. instance.service(request,response);
  7. }

Allocating the Servlet
StandardWrapperValve的invoke方法调用了包装器的allocate方法来获得一个请求servlet的实例,因此StandardWrapper类必须实现该接口
public javax.servlet.Servlet allocate() throws ServletException;

由于要支持STM servlet,这使得该方法更复杂了一点。实际上,该方法有两部分组成,一部分负责非STM servlet的工作,另一部分负责STM servlet。

第一部分的结构如下
if (!singleThreadModel) {
// returns a non-STM servlet instance
}
布尔变量singleThreadModel负责标志一个servlet是否是STM servlet。
它的初始值是false,loadServlet方法会检测加载的servlet是否是STM的,如果是则将它的值该为true
第二部分处理singleThreadModel为true的情况
synchronized (instancepool) {
// returns an instance of the servlet from the pool
}
对于非STM servlet,StandardWrapper定义一个java.servlet.Servlet类型的实例
private Servlet instance = null;
方法allocate检查该实例是否为null,如果是调用loadServlet方法来加载servlet。然后增加contAllocated整型并返回该实例。


  1. if(!singleThreadModel){
  2. if(instance==null){
  3. synchronized(this){
  4. if(instance==null){
  5. try{
  6. instance=loadServlet();
  7. }catch(ServletExceptione){
  8. throwe;
  9. }
  10. }
  11. }
  12. }
  13. if(!singleThreadModel){
  14. countAllocated++;
  15. return(instance);
  16. }

StandardWrapperValve
StandardWrapperValve是StandardWrapper实例上的基本阀,主要完成2个操作:
1,执行与该servlet的所有过滤器;(看到了吗,filter是在StandardWrapper的基础阀出现的
2,调用servlet实例的的service()方法
要实现这些内容,下面是StandardWrapperValve在他的invoke方法要实现的
. 调用StandardWrapper的allocate的方法来获得一个servlet实例
·调用它的private createFilterChain方法获得过滤链
· 调用过滤器链的doFilter方法。包括调用servlet的service方法
· 释放过滤器链
· 调用包装器的deallocate方法
· 如果Servlet无法使用了,调用Wrapper的unload方法

分享到:
评论

相关推荐

    《深入剖析 Tomcat》PDF版本下载.txt

    Tomcat作为一款开源的Servlet容器,被广泛应用于Java Web应用程序的部署与运行环境中。本资料旨在帮助读者深入了解Tomcat的工作原理及其内部机制。 ### 一、Tomcat简介 Apache Tomcat是一款免费的开源Web服务器...

    《深入剖析Tomcat(中文版+英文版)》.rar

    《深入剖析Tomcat(中文版+英文版)》.rar 《深入剖析Tomcat》深入剖析Tomcat 4和Tomcat 5中的每个组件(如果TOMCAT版本有点老,不过现在的Tomcat6和7同样可以借鉴参考),并揭示其内部工作原理。通过学习《深入剖析...

    Tomcat深入剖析pdf+源码(Tomcat运行原理)

    《Tomcat深入剖析》这本书是理解Apache Tomcat服务器工作原理的宝贵资料,它由美国作者撰写并被翻译成中文,适合各个层次的开发者阅读。通过深入学习,读者能够对Tomcat的内部机制有全面而深入的理解,从而更好地...

    深入剖析Tomcat书本源码

    《深入剖析Tomcat》这本书是Java开发者们探索Web服务器内部工作原理的重要参考资料,它详细解析了Tomcat的源代码,帮助我们理解这个流行的开源Servlet容器的运作机制。Tomcat是Apache软件基金会的一个项目,它是Java...

    深入剖析Tomcat(含源码示例)

    **深入剖析Tomcat** 《深入剖析Tomcat》是一本针对Java开发者的专业书籍,它深入探讨了Tomcat服务器的内部工作原理,旨在帮助读者理解并优化应用程序的性能。这本书以源码分析为核心,提供了丰富的示例,使读者能够...

    深入剖析Tomcat源码

    《深入剖析Tomcat源码》是一本专门为Java开发者和系统管理员设计的专业书籍,它旨在帮助读者深入了解Apache Tomcat服务器的工作原理及其源代码。Tomcat作为一款广泛应用的开源Servlet容器,其内部机制对于优化Web...

    深入剖析tomcat (完整目录)

    【深入剖析Tomcat——完整目录】 Tomcat是Apache软件基金会的Jakarta项目中的一个核心项目,是一个开源的、免费的Web应用服务器,主要用于运行Java Servlet和JavaServer Pages(JSP)。Tomcat以其轻量级、易用性...

    深入剖析TOMCAT_高清中文_带完整章节目录多版本

    《深入剖析Tomcat》是一本专门针对Java领域的Web服务器Tomcat进行深度解析的权威书籍。这本书以高清中文的形式呈现,包含完整的章节目录,旨在帮助读者全面理解和掌握Tomcat的内部工作原理及其在实际开发中的应用。...

    深入剖析Tomcat+源码

    《深入剖析Tomcat》是一本专注于Java Web服务器Tomcat的深度解析资料,包含了对Tomcat源码的细致分析。此资料包提供了多个文件,包括"深入剖析Tomcat源码.rar","深入剖析tomcat.pdf",以及"apache-tomcat-7.0.32-...

    深入剖析Tomcat 中文版 .pdf

    根据提供的信息,“深入剖析Tomcat 中文版 .pdf”这一标题和描述暗示这是一份关于Apache Tomcat服务器软件深入分析的中文文档。由于提供的部分内容主要包含重复的资源下载链接,并未涉及具体的知识点,因此本解析将...

    《深入剖析Tomcat》的光盘源码

    《深入剖析Tomcat》这本书是Java Web开发领域中的一本经典之作,主要针对Apache Tomcat服务器进行了详尽的解析和深入的研究。Tomcat是一款开源、轻量级的Servlet容器,广泛应用于各种Java Web应用程序的部署。这本书...

    深入剖析Tomcat_非扫描版

    《深入剖析Tomcat》这本书是理解Apache Tomcat服务器工作原理的重要参考资料,非扫描版的特点意味着内容清晰、可读性强,方便读者复制和引用。Tomcat作为一款广泛应用的开源Java Servlet容器,它的性能优化、配置...

    深入剖析tomcat,超清版,带标签

    【标题】:“深入剖析Tomcat,超清版,带标签” 【描述】:“深入剖析Tomcat,超清版,带标签”这一描述暗示了我们将会深入理解Tomcat服务器的内部工作机制,包括其核心原理、配置优化以及问题排查等方面。"超清版...

    《深入剖析TOMCAT》中文版的源码

    通过阅读《深入剖析TOMCAT》的源码,我们可以看到Tomcat设计的优雅之处,学习到如何优化性能,解决实际问题,甚至参与到Tomcat的社区开发中去。书中的每个章节都对应着源码中的关键部分,结合书本理论与源码实践,将...

    深入剖析Tomcat .rar

    【深入剖析Tomcat】 Tomcat是一款广泛使用的开源Java Servlet容器,由Apache软件基金会开发和维护。它是基于Java的Web应用程序的服务器,特别是用于部署和运行Java Servlets和JSP(JavaServer Pages)技术。本篇...

    深入剖析TomCat(高清版)

    《深入剖析TomCat》是一本专注于Java Web服务器Tomcat的深度学习资料,它为开发者提供了全面、详尽的TomCat内部工作原理和技术细节。这本书的高清扫描版本,旨在帮助学习者在视觉体验上得到提升,使得阅读过程更为...

    深入剖析TOMCAT+Tomcat权威指南(第二版)

    《深入剖析TOMCAT+Tomcat权威指南(第二版)》是两本关于Apache Tomcat服务器的重量级著作,它们详尽地阐述了Tomcat的内部工作机制、配置、优化以及故障排查等方面的知识,旨在帮助读者从新手到专家,全面掌握这款广泛...

    深入剖析Tomcat(中文版).pdf

    由于提供的文件内容部分信息不完整,且存在重复链接和OCR扫描技术导致的错误,以下内容将专注于对Tomcat服务器的深入剖析,并不会涉及链接信息,且尽力保证知识点的准确性和完整性。 Tomcat是Apache Jakarta项目中...

    深入剖析Tomcat 中文版 带简单书签

    在对Tomcat的这些主要组件有了初步了解之后,接下来深入剖析每一个组件的具体功能和实现方式是理解Tomcat的关键。例如: - Catalina 作为Servlet容器的核心,需要负责Servlet的生命周期管理,包括加载、初始化、...

Global site tag (gtag.js) - Google Analytics