大致架构java
Tomcat由一系列逻辑模块组织而成,这些模块主要包括: web
Service中配置了实际工做的Engine,同时配置了用来处理时间业务的线程组Executor(若是没有配置则用系统默认的WorkThread模式的线程组),以及处理网络socket的相关组件connector。apache
1) Executor是线程池,它的具体实现是java的concurrent包实现的executor,这个不是必须的,若是没有配置,则使用自写的worker thread线程池
2) Connector是网络socket相关接口模块,它包含两个对象,ProtocolHandler及Adapter 数组
容器类:tomcat提供四种容器,继承自同一个容器基类,有标准实现,也能够定制化。tomcat
配置例子安全
<Engine name="Catalina" defaultHost="localhost"> <Valve className="MyValve0"/> <Valve className="MyValve1"/> <Valve className="MyValve2"/> …… <Host name="localhost" appBase="webapps"> </Host> </Engine>
运行环境中,pipeline上的valve数组按照配置的顺序加载,可是不管有无配置定制化的valve或有多少定制化的valve,每一个容器缺省的valve,例如engine的StandardEngineValve,都会在数组中最后一个。 服务器
StandardHost的核心模块与StandardEngine差很少。只是做用域不同,它的模块只对其包含的子context有效。除此,还有一些特殊的逻辑,例如context的部署。Context的部署仍是比较多的,主要分为:网络
应该说StandardContext是tomcat中最大的一个类。它封装的是每一个web app。
看一下StandardContext的主要逻辑单元概念图。session
org.apache.catalina.session.StandardManager
manager模块是必需要有的,能够在server.xml中配置,若是没有配置的话,会在程序里生成一个manager对象。
app>web.xml:
<servlet> <servlet-name>httpserver</servlet-name> <servlet-class>com.gearever.servlet.TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>httpserver</servlet-name> <url-pattern>/*.do</url-pattern> </servlet-mapping>
对于mapper对象,能够抽象的理解成一个map结构,其key是某个访问资源,例如/*.do,那么其value就是封装了处理这个资源TestServlet的某个wrapper对象。当访问/*.do资源时,TestServlet就会在mapper对象中定位到。这里须要特别说明的是,经过这个mapper对象定位特定的wrapper对象的方式,只有一种状况,那就是在servlet或jsp中经过forward方式访问资源时用到。例如,
request.getRequestDispatcher(url).forward(request, response)
关于mapper机制会在一篇文档中专门说明,这里简单介绍一下,方便理解。如图所示。
Mapper对象在tomcat中存在于两个地方(注意,不是说只有两个mapper对象存在),其一,是每一个context容器对象中,它只记录了此context内部的访问资源与相对应的wrapper子容器的映射;其二,是connector模块中,这是tomcat全局的变量,它记录了一个完整的映射对应关系,即根据访问的完整URL如何定位到哪一个host下的哪一个context的哪一个wrapper容器。
这样,经过上面说的forward方式访问资源会用到第一种mapper,除此以外,其余的任何方式,都是经过第二种方式的mapper定位到wrapper来处理的。也就是说,forward是服务器内部的重定向,不须要通过网络接口,所以只须要经过内存中的处理就能完成。这也就是常说的forward与sendRedirect方式重定向区别的根本所在。
看一下request.getRequestDispatcher(url) 方法的源码。
Request /** * @return a RequestDispatcher that wraps the resource at the specified * path, which may be interpreted as relative to the current request path. * * @param path Path of the resource to be wrapped */ @Override public RequestDispatcher getRequestDispatcher(String path) { Context context = getContext(); if (context == null) { return null; } // If the path is already context-relative, just pass it through if (path == null) { return null; } else if (path.startsWith("/")) { return (context.getServletContext().getRequestDispatcher(path)); } // Convert a request-relative path to a context-relative one String servletPath = (String) getAttribute( RequestDispatcher.INCLUDE_SERVLET_PATH); if (servletPath == null) { servletPath = getServletPath(); } // Add the path info, if there is any String pathInfo = getPathInfo(); String requestPath = null; if (pathInfo == null) { requestPath = servletPath; } else { requestPath = servletPath + pathInfo; } int pos = requestPath.lastIndexOf('/'); String relative = null; if (context.getDispatchersUseEncodedPaths()) { if (pos >= 0) { relative = URLEncoder.DEFAULT.encode( requestPath.substring(0, pos + 1), "UTF-8") + path; } else { relative = URLEncoder.DEFAULT.encode(requestPath, "UTF-8") + path; } } else { if (pos >= 0) { relative = requestPath.substring(0, pos + 1) + path; } else { relative = requestPath + path; } } return context.getServletContext().getRequestDispatcher(relative); }
主要说说servlet对象与servlet stack对象。这两个对象在wrapper容器中只存在其中之一,也就是说只有其中一个不为空。当以servlet对象存在时,说明此servlet是支持多线程并发访问的,也就是说不存在线程同步的过程,此wrapper容器中只包含一个servlet对象(这是咱们经常使用的模式);当以servlet stack对象存在时,说明servlet是不支持多线程并发访问的,每一个servlet对象任一时刻只有一个线程能够调用,这样servlet stack实现的就是个简易的线程池,此wrapper容器中只包含一组servlet对象,它的基本原型是worker thread模式实现的。
那么,怎么来决定是以servlet对象方式存储仍是servlet stack方式存储呢?其实,只要在开发servlet类时,实现一个SingleThreadModel接口便可。
public class LoginServlet extends HttpServlet implements javax.servlet.SingleThreadModel
可是值得注意的是,这种同步机制只是从servlet规范的角度来讲提供的一种功能,在实际应用中并不能彻底解决线程安全问题,例如若是servlet中有static数据访问等,所以若是对线程安全又比较严格要求的,最好仍是用一些其余的自定义的解决方案。
Wrapper的基本功能已经说了。那么再说一个wrapper比较重要的概念。严格的说,并非每个访问资源对应一个wrapper对象。而是每一种访问资源对应一个wrapper对象。其大体可分为三种:
org.apache.catalina.servlets.DefaultServlet
org.apache.jasper.servlet.JspServlet
它主要实现了对jsp的编译等操做
须要注意的是,前两种wrapper分别是一个,主要是其对应的是DefaultServlet及JspServlet。这两个servlet是在tomcat的全局conf目录下的web.xml中配置的,当app启动时,加载到内存中。
tomcat conf web.xml <servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet> <servlet-name>jsp</servlet-name> <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class> <init-param> <param-name>fork</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>xpoweredBy</param-name> <param-value>false</param-value> </init-param> <load-on-startup>3</load-on-startup> </servlet>