Tomcat 研究之组件结构
大家好,上篇介绍了《 Tomcat 研究之 ClassLoader 》,这篇我们介绍组件结构
在没有任何实质性资料的前提下研究 Tomcat 的内核将是非常困难的事情 , 但无论如何还有 opensource, 我们至少可以跟踪 Tomcat 成百上千的类 , 为了我们能彻底解开 Tomcat 的面纱 , 我们还是要继续努力.
通过 UML 类图不难看出 ,Core 包里面最重要的一个类是 ContainerBase, 而这个抽象类实现了 Container, Lifecycle, Pipeline, MBeanRegistration, Serializable 接口 , Serializable 接口大家都已很熟悉 , 我们重在研究前几个接口 , 因为我们猜想整个 Tomcat 的部分构架可能是建立在这几个基础接口上 .
在进入分析之前我们先回忆一下 config.xml(server.xml) 文件 , 从这个文件及 apache 的文档我们大体可以了解到 Tomcat 宏观上是有下面几个部分组成:
- Server server 元素是 JVM 的入口点 , 整个配置文件只有一个 , 因为 server 不是容器 (container), 因此不能嵌套子组件 .server 在某一指定的端口监听 shutdown 命令 .server 可以包含一个或多个 service 实。
- Service service 有共享同一个 Container 的一个或多个 Connectors 组成 , 一般 Service 就是一个 Engine, 但没有明确规范要求如此 . 因为 Service 不是一个 Container, 因此不能在里面嵌套子组件 ( 比如 Loggers/Valves) 。
- Connector connector 就是一个 Tomcat 与客户端的连接 ,Tomcat 有两种典型的 Connector:http,JK2.http connector 监听来自 Browser 的连接 ( 通常在我们熟悉的 8080 端口 ),JK2. 来自其他 WebServer 的请求 ( 默认在 8009 端口监听 ) 。 Connector 会把获得的请求交给 Engine 处理。
-
Engine
Engine
下可以配置多个虚
拟
主机
Virtual Host
,
每
个虚
拟
主机都有一个域名
当
Engine
获
得一个
请
求
时
,它把
该请
求匹配到某个
Host
上,然后把
该请
求交
给该
Host
来
处
理
Engine
有一个默
认
虚
拟
主机,当
请
求无法匹配到任何一个
Host
上的
时
候,将交
给该
默
认
Host
来
处
理
- Host host 代表一个虚拟主机,默认是 localhost , host 下可以部署多个 web application ,在我们实际应用中一般要考虑问题的对象就是 host
1. org.apache.catalina.Lifecycle
通用的组件声明周期接口,一般 Tomcat 的组件都要实现这个接口(但不是必须的),这个接口是为所有组件提供相同的 start 和 stop 。主要方法有:
// 增加一个监听器
public void addLifecycleListener(LifecycleListener listener);
/**
* 这个方法应该在任何 public 方法被调用前被调用
* 该方法发送一个 START_EVENT 事件到所有注册到
* 该组件的监听器
*/
public void start() throws LifecycleException;
/**
* 这个方法应该在所有 public 方法被调用之后被调用
* 该方法发送一个 STOP_EVENT 事件到所有注册到
* 该组件的监听器
*/
public void stop() throws LifecycleException;
2. org.apache.catalina. LifecycleListener
该接口用于监听一些重要事件(包括实现了 Lifecycle 接口组件产生的 start , stop 事件)
主要方法是:
// 处理监听到的事件
public void lifecycleEvent(LifecycleEvent event);
3. org.apache.catalina . Container
容器是用于从客户端取得请求( request )并且处理请求并回复给客户端( response )的对象。容器可以支持(可选) pipeline ,以便能在运行时按配置的顺序处理请求。
在 Tomcat 里面,容器在概念上存在以下及层:
Engine 请求处理入口点,可以包含多个 Host 和 Context
Host 代表一个虚拟主机
Context 代表单个 ServletContext ,可以包含多个 Wrappers
Wrapper 代表单个 Servlet ,如果 Servlet 实现了 SingleThreadModel ,可以代表单个 Servlet 的多个实例。
容器为了实现自己的功能经常要绑定一些其他组件,这些组件的功能可能被共享,也可以被单独定制,下面是被使用的组件:
Loader ClassLoader ,装载 Java Classes
Logger 实现了 ServletContext 的 log 方法,用于记录日志
Manager 管理与容器绑定的 session 池
Realm 用户安全管理
Resources JNDI 资源访问
主要的方法:
// 增加容器监听器
public void addContainerListener(ContainerListener listener);
// 增加 property 监听器
public void addPropertyChangeListener(PropertyChangeListener listener);
/**
* 处理 Request ,并产生相应地 Response
*param request 处理的请求
*param response 产生的 response
*/
public void invoke(Request request, Response response) throws IOException, ServletException;
4. org.apache.catalina. ContainerListener
容器事件监听器,注意的是 start , stop 是正常的生命周期事件( LiftcycleEvent )不是容器事件。主要方法:
// 处理容器事件
public void containerEvent(ContainerEvent event);
5. org.apache.catalina. Pipeline
Pipleline 是 Valve 的集合,当 invoke 方法被调用时,它会按指定的顺序调用 Valve ,它总是要求有一个 Valve 必须处理传递的 request (一般是最后一个)并产生 response ,否则就把 request 传递到下一个 Valve 。
一般一个容器仅绑定一个 Pipleline 实例,一般说来容器会把处理 request 的功能封装到一个容器绑定的 Valve 里(这个 Valve 应该在 Pipleline 最后被执行)。为了完成这个功能, Pipleline 提供了 setBasic ()方法以保证 Valve 被最后执行,而其他 Valve 按顺序被调用。
6. org.apache.catalina.Valve
Valve 是被绑定在一个 Container 上的请求处理组件,一组 Valve 被按顺序绑定在一个 Pipleline 上。 Valve 最重要的一个方法是:
/**
* 一个 Valve 可能 按照一定的顺序执行下面的动作
*1. 检查并且(或者)修改指定的 Request 和 Response 属性
*2. 检查 Request 属性,生成相应的 Response 并返回控制权到调用者
*3. 检查 Request 和 Reponse 属性,包装这些对象并增强它们的功能,然后把它们传
到下一个组件
*4. 如果相应的 Response 没有被产生(并且控制权也没有被返回)调用 Pipleline
上的下一个 Valve (如果有)通过方法 context.invokeNext()
*5. 检查(但不修改) Response 属性(调用后面的 Valve 或 Container 产生的)
* Valve 一定不能作下面的事情
* 1. 改变 Request 的一些属性( Change request properties that have
already been used to direct the flow of processing control
for this request )
*2. 创建一个已经被创建并且已经被传递的 Response
*3. 在调用 invokeNext() 方法并返回后修改包含 Response 的 HTTP Header 信息
*4. 在 invokeNext() 调用返回后在绑定 Response 上的输出流上作任何调用
*@param request 将被处理的 Request
*@param response 将被创建的 Response
*@param context 被用来调用下一个 Valve 的 Valve Context
public void invoke(Request request, Response response,
ValveContext context)
throws IOException, ServletException;
7. org.apache.catalina.ValveContext
一个 ValveContext 是这样一种机制:一个 Valve 可以触发下一个 Valve 的调用,而不必知道机制的内在实现。
8. org.apache.catalina.Engine
Engine 是一个容器,是 Cataline 的 Servlet 的入口点。
当发布一个连接到 Web Server 的 Cataline 时可能不使用 Engine ,因为 Connectior 将使用 Web Server 的资源决定使用哪个 Context 处理 Request 。
附属于 Engine 的子容器根据 Engine 实现的不同可能是 Host 或 Context (单个 Servlet Context )。
如果使用了 Engine ,在 Cataline 的层次中它就是顶层容器,因此 setParent() 应改抛出 IllegalArgumentException 异常。从 Engine 下面几个方法我们可以看出其的结构:
/**
* 设定 Engine 附属的 Service
* @param service The service that owns this Engine
*/
public void setService(Service service);
/**
* Set the default hostname for this Engine.
*
* @param defaultHost The new default host
*/
public void setDefaultHost(String defaultHost);
/**
* Set the DefaultContext
* for new web applications.
*
* @param defaultContext The new DefaultContext
*/
public void addDefaultContext(DefaultContext defaultContext);
9. org.apache.catalina. Host
Host 是一个容器,它代表一个虚拟主机。
当发布一个连接到 Web Server 的 Cataline 时可能不使用 Host ,因为 Connectior 将使用 Web Server 的资源决定使用哪个 Context 处理 Request 。
Host 所附属的父容器通常是 Engine ,附属于 Host 的子容器通常是 Context (单个 Servlet Context )。
Host 接口里面的方法多数都是关于修改 Host 属性及设定默认的 Context 。这里我们不再一一列举。
10. org.apache.catalin. Context
Context 是一个容器,它代表一个 ServletContext ,一个 Cataline Engline 中的单个的 Web Application 。 Context 所附属的父容器是 Host ,附属于 Context 的子容器是 Wrapper (代表单个 Servlet )。 Context 接口里面多数是关于 Web Application 的设置的方法,我们可以参考 Web.xml 文件研究里面的方法,里面多数方法都是如何读取 Web.xml 文件里的资源。
11. org.apache.catalina.Wrapper
Wrapper 是一个容器,它代表单个 Servlet 。 Wrapper 管理 Servlet 的生命周期,包括调用 init ()和 destory ()方法。
Wrapper 所附属的父容器是 Context ,没有附属于 Wrapper 的子容器,方法 addChild() 应该抛出 IllegalArgumentException 异常。
Wrapper 接口里面的方法都是关于读取 Servlet 的属性,可以参考 Web.xml 文件里面关于 <servlet> 标签的定义。
12. org.apache.catalina.Server
Server 是整个 Catalina 容器的入口点,可以包含多个 Service 和顶层资源元素。一般说来实现 Server 接口的类也应该同时实现 Lifecycle 接口,当 start ()和 stop ()方法被调用的时候调用 Service 相应的方法。
Server 负责在指定的端口监听连接,当有连接被接受的时候, Server 会分析请求的第一行信息,如果是 SHUTDOWN 则 stop 服务。可以参考 Server.xml 文件关于 Server 的定义。
13. org.apache.catalina. Service
Service 是一个或多个共享同以 Container 的 Connectiors 的集合。
JVM 可以包含一个或多个 Service 实例,但它们相互之间是完全独立的,它们仅共享 JVM 的资源。
14. org.apache.catalina. Connector
Connector 是一个从客户端接受请求( request )并生成回应( reponse )的组件。一个 Connection 通常执行下面的逻辑:
1) 从客户端程序接受请求
2) 创建 Request 和 Response ,并把下面这些属性设置到这些对象
( 1 )对所有的 Request , connector , protocol , protocol , response , scheme , secure , serverName , serverPort , serverPort 属性必须被设置。 contentLength , contentType 通常也被设置。
3) 对所有的 HttpRequests , method , queryString , requestedSessionCookie , requestedSessionId , requestedSessionURL , requestURI , secure 属性必须被设置。另外所有 addXxx 方法也必须被调用以记录 cookies , headers 和 locales 信息。
4) 对所有的 Responses 属性 connector , request , stream 属性必须被设置。
对 HttpResponses 来说, connector 不会为它设置额外 headers 信息。
1. Tomcat 整体架构类图
下篇将从 Tomcat 的各个组件入手介绍 Tomcat 。