一、Tomcat介绍
开源的java web应用服务器,实现了java EE的部分技术规范,如 java servlet、javaServer Pages、 JavaWebSocket等;
核心:http服务器+Servlet容器
二、Tomcat两个核心功能
1、处理Socket连接,负责网络字节流与Request和Response对象的转化
2、 加载和管理servlet,以及具体处理Request请求。
因此,Tomcat设计了两个核心组件,连接器(Connector)和容器(Container),连接器负责处理对外交流,容器负责内部处理;
三、核心组件
1、Server组件
整个Tomcat服务器,包含多组服务,负责管理和启动各个service,同时监听8005端口发来的shutdown命令,用于关闭整个容器;
2、 Service组件
每个Service组件都包含了若干个用于接收客户端的Connector组件和处理请求的Enigine组件。Service组件还包含了若干个Executor组件,每个Executor都是一个线程池,它可以为Service内所有组件提供线程池执行任务。
Tomcat为了实现多种IO模型和应用层协议,一个容器可能对接多个连接器,单独的连接器或者容器都不能对外提供服务,需要把他们组装起来才能工作,组装后的整体叫做Service组件;
Tomcat内可能有多个Service,这样的设计也是出于灵活性考虑。通过Tomcat中配置多个Service,可以实现通过不同的端口来访问同一台机器上部署的不同应用。
最顶层是Server(Tomcat实例),一个Server中有一个或多个Service,一个Server有多个连接器和一个容器;连接器和容器通过ServletRequest和ServletResponse通信;
3、连接器Connector组件
Tomcat 与外部世界的连接器,监听固定端口接收外部请求,传递给 Container,并将
Container 处理的结果返回给外部。
连接器对Servlet容器屏蔽了不同的应用层协议及IO模型,无论是HTTP还是AJp,容器中都是获取到一个标准的ServletRequest对象;
连接器功能:
a、监听网络端口
b、接收网络连接请求
c、读取请求字节流
d、根据具体协议解析字节流,生成统一对象
e、将Tomcat Request对象转换为ServletRequest对象
f、调用Servlet容器获得ServletResponse
g、将ServletResponse转换为Tomcat Response对象
h、将 Tomcat Response 转成网络字节流
将上述功能按照高内聚低耦合进行分析,可分为三个功能:
网络通信,应用层协议解析,Tomcat Request/Response 与 ServletRequest/ServletResponse 的转化;
按照上述拆分对应的三个功能,分别是EndPoint、Processor和Adapter:
EndPoint 负责提供字节流给 Processor;
Processor 负责提供 Tomcat Request 对象给 Adapter;
Adapter 负责提供 ServletRequest 对象给容器。
组件之间通过接口进行交互,变化与非变化部分隔离,增加复用性,降低耦合度;
Tomcat的设计者将网络通信和应用层协议解析放在一起,设计了ProtocolHandler接口;各种协议和通信模型的组合有响应的实现类;
Tomcat还设计了抽象基类来封装稳定的部分;抽象基类AbstractProtocol实现了ProtocolHandler接口;
4、ProtocolHandler组件
连接器用ProtocolHandler来处理网络协议和应用层协议,包含了两个重要部分:EndPoint和Processor;
Endpoint 负责调用底层socket通信,Proccessor负责应用层协议解析;
EndPoint 是一个接口,对应的抽象实现类是 AbstractEndpoint,而AbstractEndpoint 的具体子类,比如在 NioEndpoint 和 Nio2Endpoint 中,有两个重要的子组件:Acceptor 和 SocketProcessor。其中 Acceptor 用于监听 Socket 连接请求。SocketProcessor 用于处理接收到的 Socket 请求,它实现 Runnable 接口,在 Run 方法里调用协议处理组件 Processor 进行处理。为了提高处理能力,SocketProcessor 被提交到线程池来执行,而这个线程池叫作执行器(Executor)。
Processor 用来实现 HTTP/AJP 协议,Processor 接收来自 EndPoint 的 Socket,读取字节流解析成 Tomcat Request 和 Response 对象,并通过 Adapter 将其提交到容器处理,Processor 是对应用层协议的抽象。Processor 是一个接口,定义了请求的处理等方法。它的抽象实现类AbstractProcessor 对一些协议共有的属性进行封装,没有对方法进行实现。具体的实现有AJPProcessor、HTTP11Processor 等,这些具体实现类实现了特定协议的解析方法和请求处理方式。
EndPoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 Run 方法会调用 Processor 组件去解析应用层协议,Processor通过解析生成 Request 对象后,会调用 Adapter 的 Service 方法。
5、Adapter组件
ProtocolHandler接口负责解析请求并生成Tomcat Request,Tomcat设计者引入CoyoteAdapter,这是适配器模式的经典运用;连接器调用CoyoteAdapter的Service方法传入Tomcat Request对象,CoyoteAdapter负责将对象转为ServletRequest,再调用容器中的Service方法;
6、容器组件
容器,顾名思义就是用来装载东西的器具,在 Tomcat 里,容器就是用来装载 Servlet的。Tomcat 通过一种分层的架构,使得 Servlet 容器具有很好的灵活性。Tomcat 设计了4 种容器,分别是 Engine、Host、Context 和 Wrapper。这 4 种容器不是平行关系,而是父子关系。
Engine:引擎,Servlet 的顶层容器,用来管理多个虚拟站点,一个Service 最多只能有一个 Engine;
Host:虚拟主机,负责 web 应用的部署和 Context 的创建。可以给Tomcat 配置多个虚拟主机地址,而一个虚拟主机下可以部署多个 Web 应用程序;
Context:Web 应用上下文,包含多个 Wrapper,负责 web 配置的解析、管理所有的 Web 资源。一个Context对应一个 Web 应用程序。
Wrapper:表示一个 Servlet,最底层的容器,是对 Servlet 的封装,负责Servlet 实例的创建、执行和销毁。
四、整体架构图
五、Tomcat工作原理
Tomcat 是用 Mapper 组件来完成这个任务的。Mapper 组件的功能就是将用户请求的 URL 定位到一个 Servlet,它的工作原理是:Mapper 组件里保存了 Web 应用的配置信息,其实就是容器组件与访问路径的映射关系,比如 Host 容器里配置的域名、Context 容器里的 Web 应用路径,以及 Wrapper 容器里 Servlet 映射的路径,你可以想象这些配置信息就是一个多层次的 Map。当一个请求到来时,Mapper 组件通过解析请求 URL 里的域名和路径,再到自己保存的 Map 里去查找,就能定位到一个 Servlet。一个请求 URL 最后只会定位到一个 Wrapper 容器,也就是一个 Servlet。
1、请求在容器中的调用过程
连接器中的 Adapter 会调用容器的 Service 方法来执行 Servlet,最先拿到请求的是Engine 容器,Engine 容器对请求做一些处理后,会把请求传给自己子容器 Host 继续处理,依次类推,最后这个请求会传给 Wrapper 容器,Wrapper 会调用最终的 Servlet 来处理。那么这个调用过程具体是怎么实现的呢?答案是使用 Pipeline-Valve 管道。
Pipeline-Valve 是责任链模式,责任链模式是指在一个请求处理的过程中有很多处理者依次对请求进行处理,每个处理者负责做自己相应的处理,处理完之后将再调用下一个处理者继续处理。
public interface Valve {
public Valve getNext();
public void setNext(Valve valve);
public void invoke(Request request, Response response) throws IOException,ServletException;
}
public interface Pipeline {
public Valve getBasic();
public void setBasic(Valve valve);
public void addValve(Valve valve);
public Valve getFirst();
}
Pipeline 中维护了 Valve 链表,Valve 可以插入到 Pipeline 中,对请求做某些处理。整个调用链的触发是 Valve 来完成的,Valve 完成自己的处理后,调用 getNext.invoke()来触发下一个 Valve 调用。每一个容器都有一个 Pipeline 对象,只要触发这个 Pipeline 的第一个 Valve,这个容器里 Pipeline 中的 Valve 就都会被调用到。Basic Valve 处于 Valve链表的末端,它是 Pipeline 中必不可少的一个 Valve,负责调用下层容器的 Pipeline 里的第一个 Valve。