Server、Service、Connector、Container、Engine、Host、Context,结构见server.xml
Context容器中的Component:
manager: 支持session 管理
logger: logs/catalog_log.time.txt
logs/localhost.time.txt
logs/localhost_examples_log.time.txt
loader: 启动context,管理该Context的classloader
pipeline:
valve:
其中,Connector中包含httpConnector和AJP
JMX技术(对象以及调用方法)
1、 MBean: 用来映射对象
2、 agent: 通过它,可以找到MBean
3、 connector:连接agent的方式,如http、rmi或socket
1 GlobalResourcelifecycleListener类
Protectedstatic Registry registry = MBeanUtils.createRegistry();运行
MBeanUtils.createRegistry()会依据/org/apache/catalina/mbeans/mbeans-descriptors.xml来创建mbeans
2 Catalina中start()有一个ShutdownHook,检测当server shutdown 时,关闭tomcat的各个容器
3 Server: before_start、start、after_start三个事件
Service:启动Engine、Connector
Engine以及以下级别的容器:
4、 运行各个容器自己特有的一些任务
5、 触发before_start事件
6、 设置标签,表示该容器已经启动
7、 启动容器中的各个组件:loader、logger、manager等
8、 启动mapping组件,此组件的用处是当一个父容器传递给子容器需求时,父容器有多个子容器,组件用于选择使用哪个子容器来处理。
9、 启动子容器
10、 启动该容器的pipeline
11、 start、after_start事件
Engine、Host、Context都是这样做的,代码复用使用抽象类,ContainerBase重点查看
4 Host
org.apache.catalina.valves.ErrorReportValve
作用:需求在被Engine传递给Host后, 会继续传递给Context做具体的处理。 这里需求其实就是作为参数传递的 Request, Response。所以在context把需求处理完后,通常会改动response。而这个 org.apache.catalina.valves.ErrorReportValve的作用就是检察response是否包含错误, 如果有就做相应的处理。
5 Context
StandardContext.start() 这个启动Context容器的方法被StandardHost调用.
12、 webappResources 该context所指向的具体目录
13、 安装defaultContex, DefaultContext 就是默认Context。 如果我们在一个Host下面安装了DefaultContext,而且defaultContext里面又安装了一个数据库连接池资源的话。那么其他所有的在该Host下的 Context, 都可以直接使用这个数据库连接池,而不用格外做配置了。
14、 指定Loader. 通常用默认的org.apache.catalina.loader.WebappLoader这个类。 Loader就是用来指定这个context会用到哪些类啊, 哪些jar包啊这些什么的。
15、 指定 Manager. 通常使用默认的org.apache.catalina.session. StandardManager 。 Manager是用来管理session的。其 实session的管理也很好实现。 以一种简单的session管理为例。 当需求传递过来的时候, 在Request对象里面有一个sessionId 属性。 OK,得到这个sessionId后,我们就可以把它作为map的key,而value我们可以放置一个 HashMap. HashMap里边儿,再放我们想放的东西。sessionid生成参见ManagerBase.generateSessionId()
16、 postWorkDirectory (). Tomcat下面有一 个work目录。 我们把临时文件都扔在那儿去。 这个步骤就是在那里创建一个目录。 一般说来会在%CATALINA_HOME%/work/Standalone/localhost/ 这个地方生成一个目录。
17、 Binding thread。到了这里, 就应该发 生 class Loader 互换了。 之前是看得见tomcat下面所有的class和lib. 接下来需要看得见当前context下的 class。 所以要设置contextClassLoader,同时还要把旧的ClassLoader记录下来,因为以后还要用的。
18、 启动 Loader. 指定这个Context具体要使用哪些classes,用到哪些jar文件。如果reloadable设置成了true, 就会启动一个线程来监视classes的变化,如果有变化就重新启动Context。
19、 启动logger
20、 触发安装在它身上的一个监听器。lifecycle.fireLifecycleEvent(START_EVENT, null); 作为监听器之一,ContextConfig会被启动.ContextConfig就是用来配置web.xml的。比如这个Context有多少Servlet,又有多少Filter,就是在这里给Context装上去的。
21、 defaultConfig. 每个context都得配置tomcat/conf/web.xml 这个文件。
22、 applicationConfig 配置自己的WEB-INF/web.xml 文件
23、 validateSecurityRoles 权 限验证。 通常我们在访问/admin 或者/manager的时候,需要用户要么是admin的要么是manager的, 才能访问。 而且我们还可以 限制那些资源可以访问, 而哪些不能。 都是在这里实现的。
24、 tldScan: 扫描一下, 需要用到哪些标签(tag lab)
25、 启动 manager
26、 postWelcomeFiles() 我们通常会用到的3个启动文件的名称:index.html、index.htm、index.jsp 就被默认地绑在了这个context上
27、 listenerStart 配置listener
28、 filterStart 配置 filter
29、 启动带有1的Servlet.顺序是从小到大: 1,2,3…最后是0
默认情况下,至少会启动如下3个的Servlet:
org.apache.catalina.servlets.DefaultServlet
处理静态资源的Servlet. 什么图片啊, html啊, css啊, js啊都找他
org.apache.catalina.servlets.InvokerServlet
处理没有做Servlet Mapping的那些Servlet.
org.apache.jasper.servlet.JspServlet
处理JSP文件的.
30、 标识context已经启动完毕。
走了多少个步骤啊, Context总算是启动完毕喽。
OK! 走到了这里,每个容器以及组件都启动完毕。 Tomcat终于不辞辛劳地为人民服务了!
3. 参考文献:
<http://jakarta.apache.org/tomcat/>;
<http://www.onjava.com/pub/a/onjava/2003/05/14/java_webserver.html>;
Tomcat的架构总的来说是分层次的、可插拔的组件架构。分层次是指构成Tomcat的组件不是同一级别的,上层组件可以包含子组件,各个组件有其功能范围,当一个组件停止服务时,不会影响上层组件的服务。可插拔是指对于组件的添加和删除并不影响服务器的运行。那么为了达到可插拔的组件架构,分层次的组件架构必成为基础。
对于任何服务器,即使最简单的实现,从面向对象设计(OOD)的角度来说,我们都有必要将“服务器”这个概念抽象出来,为什么呢?因为只有有了这个概念,才能谈服务器的实例,服务器的功能等等其它概念,此之谓“皮之不存,毛将焉附”。赶巧(其实是我的想法恰好撞上人家的想法),Tomcat也将“服务器”抽象为java接口org.apache.catalina.Server,显然Server应该就是最最顶层的组件了。
有了Server这个抽象,很自然的,我们希望它能够提供对servlet和jsp支持的功能。但是我们发现这个概念太大了,我们还需再细化。所以别急,我们还有一些事情要解决。服务器要提供服务就必须能够启动,当然也应该能够停止吧,也就是说服务器应该是有生命的,在启动时初始化必要的资源,而在停止时将其其销毁掉。好吧,我们把这个也抽象出来,叫做生命周期接口,tomcat 实现为org.apache.catalina.Lifecycle.如上所述我们知道Lifecycle需要完成的工作了。
public void start() throwsLifecycleException;
public void stop() throwsLifecycleException;
接下来我们分析服务器如何来处理客户端的请求,一般的我们会在浏览器中输入如下格式的请求,http://192.168.8.221:8080/explorer/loginInit.do。对于服务器来说,要想处理这个请求,就必须监听指定的端口8080,当有TCP的请求包来时,建立Socket连接,分析并解析之,然后给客户端返回响应。在这个过程中,我们发现,其实包含了俩个功能点,即监听并接受请求和处理请求。那么我们能否将这俩个功能给抽象出来呢?Tomcat告诉我们,可以。是的,Tomcat将“监听并接收请求”抽象为org.apache.catalina.connector.Connector类,负责接受请求;将“处理请求”抽象为“容器” org.apache.catalina.Container,负责处理Connector传递过来的请求。
Ok,到此,我们分析构建的简单服务器模型出来了,Server由Connector组件和Container组件结合提供web服务。
有了这个模型后,要实现一个简单的Server已经很简单了,但是在实现Container时,我们还是要做很多事情,如当来请求,我们怎么知道该请求对应得虚拟主机,以及请求的那个应用,应该交给那个servlet对象来处理?这样看来,Container还是太大了,需要细化。根据Servlet规范,我们知道,servlet属于某个应用,且有上下文环境,Container要根据应用上下文环境初始化servlet,然后根据servlet映射调用servlet的service方法。在这里“应用上下文环境”的概念很重要,Tomcat将其抽象为org.apache.catalina.Context,Context继承了Container接口。对于虚拟主机,Tomcat将其抽象为org.apache.catalina.Host,Host继承了Container接口。
好了,有了这些概念,我们再回顾一下请求的处理过程:浏览器发出请求,Connector接受请求,将请求交由Container处理,Container查找请求对应的Host并将请求传递给它,Host拿到请求后查找相应的应用上下文环境,准备servlet环境并调用service方法。
但是在Tomcat的实现体系中还有一个Engine的接口,Engine也继承了Container接口,那么这个接口什么用呢?设计Engine的目的有俩个目的,一,当希望使用拦截器查看(过滤或预处理)每个请求时,Engine是个很好的拦截点。二,当希望多个虚拟Host共享一个Http的Connector时,Engine是个很好的门面。所以,Engine接口是作为顶级Container组件来设计的,其作用相当于一个Container的门面。有了Engine,请求的处理过程变为:浏览器发出请求,Connector接受请求,将请求交由Container(这里是Engine)处理,Container(Engine来担当)查找请求对应的Host并将请求传递给它,Host拿到请求后查找相应的应用上下文环境,准备servlet环境并调用service方法。
到目前,我们碰到的组件类型有Connector和Container,其实,这也就是Tomcat的核心组件。一组Connector和一个Container有机的组合在一起构成Server,就可以提供服务了,对于Tomcat来说,主要是提供Servlet服务,那么也就是说Tomcat服务器也可以提供其它服务了?是的,Tomcat将“一组Connector和一个Container有机的组合”抽象为“服务”接口org.apache.catalina.Service,然而,这些服务实例彼此独立,仅仅共享JVM的基础设施,如系统类路径。
对于Tomcat服务器来说,除了Server代表它自己以外,其它组件都是功能组件,都有其职责范围。Service为最顶层的组件,可以添加Connector和Container组件。Engine是Container的最顶层组件,可以添加Host组件,但不能添加父组件。Host组件的父组件是Engine,Host下面包含有Context组件。
参考http://blog.csdn.net/xiaojianpitt/article/details/4865531