一、tomcat需要的核心功能
- 处理socket连接
- 负责字节流到request和response对象的转化
- 加载和管理servlet
- request请求的具体处理过程
二、tomcat两大核心组件
-
连接器connector
负责对外交流,处理上边前两件事情
-
容器container
容器负责内部处理,处理上边后两件事情
三、 tomcat支持的IO模型与协议
- IO模型
- NIO,非阻塞IO,采用java类库实现
- NIO2,异步IO,jdk7以上类库的实现,也叫AIO,需要操作系统支持,目前linux并不支持
- ARP,采用apache可移植库实现,C/C++本地库
- 协议
- HTTP/1.1:这是大部分 Web 应用采用的访问协议
- HTTP/2:HTTP 2.0 大幅度的提升了 Web 性能
- AJP:用于和 Web 服务器集成如与apache服务器通信
四、tomcat总体架构
-
基于以上的IO模型和协议,可以组合出不同的连接器
-
一个容器可以对接多个连接器
-
容器和连接器都不能单独对外提供服务
-
容器和连接器组装成service组件对外提供服务,service本身并不做任何事情
-
一个tomcat server可以配置多个service,使用不同的端口来访问同一台机器上多个应用
tomcat整体架构.jpg
如图最顶层是一个server,代表一个tomcat实例,包含多个service,一个service包含多个连接器和一个容器,连接器和容器使用标准的ServletRequest和ServletResponse对象通信
-
连接器
-
工作流程
- 监听网络端口
- 接收网络请求
- 读取请求字节流
- 根据应用协议(AJP/Http)解析字节流,生成统一的TomcatRequest对象
- 转换TomcatRequest对象为ServletRequest
- 调用servlet容器得到ServletResponse对象
- 将ServletResponse对象转为TomcatResponse
- 将TomcatResponse转为网络字节流
- 将网络字节流返回给浏览器
-
设计思想
体现的是一种高内聚、低耦合的思想,有以下三个高内聚的功能
- 网络通信
- 应用层协议解析
- Tomcat Request/Response对象与Servlet Request/Response对象的转换
组件之间的通过抽象接口交互,好处是封装变化,有助于增加复用降低代码耦合度。
对应三个功能tomcat分别设计了三个组件来对应,分别是Endpoint、Processor和Adapter
变化的部分是IO模型与通信协议,不变的部分是处理流程
Endpoint 负责提供字节流给 Processor,Processor 负责提供 Tomcat Request 对象给 Adapter,Adapter 负责提供 ServletRequest 对象给容器。
connecor与container交互.jpg
-
继承关系说明
连接器类继承关系.jpg
-
-
ProtocolHandler定义处理网络连接和应用层协议的接口
-
抽象基类 AbstractProtocol 实现了 ProtocolHandler 接口
-
每个协议又有自己的抽象基类如 AbstractAjpProtocol 和 AbstractHttp11Protocol
-
针对每一种IO模型与协议又可以组成不同的实现类如Http11NioProtocol、Http11NioProtocol
-
connector组件工作流程详解
-
ProtocolHandler 组件,包含Endpoint和Processor两个子组件
Endpoint 具体的通信端点,通信监听接口,是Socket的接收和发送处理器,用来实现 TCP/IP 协议的
Endpoint 是一个接口,对应的抽象实现类是 AbstractEndpoint,而 AbstractEndpoint 的具体子类,比如在 NioEndpoint 和 Nio2Endpoint 中,有两个重要的子组件Acceptor 和 SocketProcessor。
Acceptor 用于监听socket连接
SocketProcessor处理socket请求,它实现runable接口在 run 方法里调用协议处理组件 Processor 进行处理为了提高处理能力,SocketProcessor 被提交到线程池来执行
Processor接收来自 Endpoint 的 Socket,读取字节流解析成 Tomcat Request 和 Response 对象,并通过 Adapter 将其提交到容器处理,Processor 是对应用层协议的抽象。具体根据不同协议和IO模型调用不同的Processor实现类执行
-
Apapter组件 见名知意,经典的适配器模式的实现
请求经ProtocolHandler处理后统一转换为tomcat自定义的TomcatRequest对象而不是标准的ServletRequest,Apapter接口的实现类CoyoteAdapter的作用就是将TomcatRequest转为ServletRequest对象并调用容器的service交给容器处理请求
-
-
-
容器
容器.jpg -
容器层次结构
Tomcat 设计了 4 种容器,分别是 Engine、Host、Context 和 Wrapper。这 4 种容器是父子关系。这种分层的架构,使得 Servlet 容器具有很好的灵活性。
Engine上面tomcat总体架构图里的Service里的容器就是Engine,一个Service最多一个Engine
Host代表一个虚拟主机,可以给tomcat配置多个虚拟主机地址
Context代表一个web应用,一个Host下可以有多个web应用
Wrapper就是一个Servlet一个web应用下可以有多个servlet
下面通过tomcat server.xml的配置图说明进一步加深了解
tomcat如何管理容器
这些容器具有父子关系,形成一个树形结构,你可能马上就想到了设计模式中的组合模式
没错,Tomcat 就是用组合模式来管理这些容器的。
- 所有容器都实现了Container 接口,对外提供一致性使用方式
- 内部提供对parent、child对象的操作,可实现完整的树形结构的操作
public interface Container extends Lifecycle {
public void setName(String name);
public Container getParent();
public void setParent(Container container);
public void addChild(Container child);
public void removeChild(Container child);
public Container findChild(String name);
}
-
同时发现Container还继承了Lifecycle接口,这是与tomcat生命周期相关的接口,以后再讲
-
请求定位servlet过程
请求定位是通过mapper组件来完成,源码路径是org.apache.catalina.mapper
- mapper组件保存了web应用的配置信息,比如 Host 容器里配置的域名、Context 容器里的 Web 应用路径,以及 Wrapper 容器里 Servlet 映射的路径,你可以想象这些配置信息就是一个多层次的 Map
- 请求到来时,Mapper 组件通过解析请求 URL 里的域名和路径,再到自己保存的 Map 里去查找,就能定位到一个 Servle
举个例子描述下请求定位过程
假设同一个tomcat下配置了两个域名,manage.shopping.com和user.shopping.com
网站管理人员通过manage.shopping.com域名访问 Tomcat 去管理用户和商品,而用户管理和商品管理是两个单独的 Web 应用。终端客户通过user.shopping.com域名去搜索商品和下订单,搜索功能和订单管理也是两个独立的 Web 应用。
针对这样的部署,Tomcat 会创建一个 Service 组件和一个 Engine 容器组件,在 Engine 容器下创建两个 Host 子容器,在每个 Host 容器下创建两个 Context 子容器。
请求定位过程.jpg
-
假如有用户访问一个 URL,比如图中的http://user.shopping.com:8080/order/buy,Tomcat 如何将这个 URL 定位到一个 Servlet 呢?
-
根据协议和端口号选定 Service 和 Engine
本例中只有一个Service/Engine默认部署在8080端口
-
根据域名选定 Host
本例user.shopping.com选定host2
-
根据 URL 路径找到 Context 组件
本例order选定context4
-
根据 URL 路径找到 Wrapper(Servlet)
本例buy找到Wrapper7对应servlet
-
-
容器是如何关联一级一级定位的
Adapter 会调用容器的 Service 方法来执行 Servlet,最先拿到请求的是 Engine 容器,Engine 容器对请求做一些处理后,会把请求传给自己子容器 Host 继续处理,依次类推,最后这个请求会传给 Wrapper 容器,Wrapper 会调用最终的 Servlet 来处理。
tomcat使用 Pipeline-Valve 管道来实现,这是典型的责任链模式,责任链模式是指在一个请求处理的过程中有很多处理者依次对请求进行处理,每个处理者负责做自己相应的处理,处理完之后将再调用下一个处理者继续处理。
Value表示一个处理点,比如权限认证和记录日志
public interface Valve {
public Valve getNext();
public void setNext(Valve valve);
public void invoke(Request request, Response response)
}
注意代码中的setNext和getNext将多个处理点连接起来组成链表,invoke方法是执行具体的处理点
Pipeline
public interface Pipeline extends Contained {
public void addValve(Valve valve);
public Valve getBasic();
public void setBasic(Valve valve);
public Valve getFirst();
}
- Pipeline 中维护了 Valve 链表,Valve 可以插入到 Pipeline 中。 Pipeline 中没有 invoke 方法,因为整个调用链的触发是 Valve 来完成的,Valve 完成自己的处理后,调用getNext.invoke来触发下一个 Valve 调用。
- 每一个容器都有一个 Pipeline 对象,只要触发这个 Pipeline 的第一个 Valve,这个容器里 Pipeline 中的 Valve 就都会被调用到。 Pipeline 中还有个 getBasic 方法。这个 BasicValve 处于 Valve 链表的末端,它是 Pipeline 中必不可少的一个 Valve,负责调用下层容器的 Pipeline 里的第一个 Valve。
-
整个调用过程由连接器中的 Adapter 触发的,它会调用 Engine 的第一个 Valve
pipline-value.jpg
// Calling the container
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
补充说明
Wrapper 容器的最后一个 Valve 会创建一个 Filter 链,并调用 doFilter 方法,最终会调到 Servlet 的 service 方法。
Valve与Filter区别
-
Valve 是 Tomcat 的私有机制,与 Tomcat 的基础架构 /API 是紧耦合的。
-
Servlet API 是公有的标准,所有的 Web 容器包括 Jetty 都支持 Filter 机制。
-
Valve 工作在 Web 容器级别,拦截所有应用的请求。
-
而 Servlet Filter 工作在应用级别,只能拦截某个 Web 应用的所有请求。
网友评论