Tomcat6.0源码学习--架构概述 博客分类: Tomcat 源码研究 Tomcat应用服务器ServletApache浏览器
Tomcat6 是最新版本的 web 容器,其支持最新版本的 servlet2.5 和 jsp2.1 。而且 Tomcat6 架构也是经过重新设计优化过的,所以我们有必要分析一下它的架构过程。显然,这是一个通过阅读 Tomcat 的源代码及相关文档,演绎架构的过程。或许有人会说,这不是放马后炮吗?!!但我觉得这是自我进步的一个必经步骤,先模仿之,然后才能超越之,毕竟我本凡人。
Tomcat 的架构总的来说是分层次的、可插拔的组件架构。分层次是指构成 Tomcat 的组件不是同一级别的,上层组件可以包含子组件,各个组件有其功能范围,当一个组件停止服务时,不会影响上层组件的服务。可插拔是指对于组件的添加和删除并不影响服务器的运行。那么为了达到可插拔的组件架构,分层次的组件架构必成为基础。
对于任何服务器,即使最简单的实现,从面向对象设计( OOD )的角度来说,我们都有必要将“服务器”这个概念抽象出来,为什么呢?因为只有有了这个概念,才能谈服务器的实例,服务器的功能等等其它概念,此之谓“皮之不存,毛将焉附”。赶巧( 其实是我的想法恰好撞上人家的想法 ), Tomcat 也将“服务器”抽象为 java 接口 org.apache.catalina.Server ,显然 Server 应该就是最最顶层的组件了。
有了 Server 这个抽象,很自然的,我们希望它能够提供对 servlet 和 jsp 支持的功能。但是我们发现这个概念太大了,我们还需再细化。所以别急,我们还有一些事情要解决。服务器要提供服务就必须能够启动,当然也应该能够停止吧,也就是说服务器应该是有生命的,在启动时初始化必要的资源,而在停止时将其其销毁掉。好吧,我们把这个也抽象出来,叫做生命周期接口, tomcat 实现为 org.apache.catalina.Lifecycle. 如上所述我们知道 Lifecycle 需要完成的工作了。
public void start () throws LifecycleException; public void stop() throws LifecycleException; |
接下来我们分析服务器如何来处理客户端的请求,一般的我们会在浏览器中输入如下格式的请求, 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 服务。
图 2
有了这个模型后,要实现一个简单的 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 方法。
现在,我们的服务器模型变成了如图 3 所示了。
图 3
但是在 Tomcat 的实现体系中还有一个 Engine 的接口, Engine 也继承了 Container 接口,那么这个接口什么用呢?设计 Engine 的目的有俩个目的,一,当希望使用拦截器查看(过滤或预处理)每个请求时, Engine 是个很好的拦截点。二,当希望多个虚拟 Host 共享一个 Http 的 Connector 时, Engine 是个很好的门面。所以, Engine 接口是作为* Container 组件来设计的,其作用相当于一个 Container 的门面。有了 Engine ,请求的处理过程变为:浏览器发出请求, Connector 接受请求,将请求交由 Container (这里是 Engine )处理, Container ( Engine 来担当)查找请求对应的 Host 并将请求传递给它, Host 拿到请求后查找相应的应用上下文环境,准备 servlet 环境并调用 service 方法。再看看服务器的模型,如图 4.
图 4
到目前,我们碰到的组件类型有 Connector 和 Container ,其实,这也就是 Tomcat 的核心组件。如图 4 ,一组 Connector 和一个 Container 有机的组合在一起构成 Server ,就可以提供服务了,对于 Tomcat 来说,主要是提供 Servlet 服务,那么也就是说 Tomcat 服务器也可以提供其它服务了?是的, Tomcat 将“一组 Connector 和一个 Container 有机的组合”抽象为“服务”接口 org.apache.catalina.Service ,然而,这些服务实例彼此独立,仅仅共享 JVM 的基础设施,如系统类路径。
进一步的,我们得到了服务器的框架模型,如图 5.
图 5
由图 5 ,我们知道,对于 Tomcat 服务器来说,除了 Server 代表它自己以外,其它组件都是功能组件,都有其职责范围。 Service 为最顶层的组件,可以添加 Connector 和 Container 组件。 Engine 是 Container 的最顶层组件,可以添加 Host 组件,但不能添加父组件。 Host 组件的父组件是 Engine , Host 下面包含有 Context 组件。
接下来看看标准的 Tomcat 体系结构,如图 6.
图 6
比较图 5 和图 6. 我们发现,还有很多辅助组件没有抽象出来。当然,随着需求的一步步加深,我的分析也会一步步深入,这些个组件也会慢慢浮出水面。