我们知道如果要设计一个系统,首先是要了解需求。通过专栏前面的文章,我们已经了解了 Tomcat 要实现 2 个核心功能:
处理 Socket 连接,负责网络字节流与 Request 和 Response 对象的转化。加载和管理 Servlet,以及具体处理 Request 请求。因此 Tomcat 设计了两个核心组件连接器(Connector)和容器(Container)来分别做这两件事情。连接器负责对外交流,容器负责内部处理。连接器用 ProtocolHandler 接口来封装通信协议和 I/O 模型的差异,内部又分为 Endpoint 和 Processor 模块,Endpoint 负责底层 Socket 通信,Processor 负责应用层协议解析。连接器通过适配器 Adapter 调用容器。

Tomcat 支持的多种 I/O 模型和应用层协议。Tomcat 支持的 I/O 模型有:NIO:非阻塞 I/O,采用 Java NIO 类库实现。
NIO.2:异步 I/O,采用 JDK 7 最新的 NIO.2 类库实现。
APR:采用 Apache 可移植运行库实现,是 C/C++ 编写的本地库。
Tomcat 支持的应用层协议有:
HTTP/1.1:这是大部分 Web 应用采用的访问协议。
AJP:用于和 Web 服务器集成(如 Apache)。
HTTP/2:HTTP 2.0 大幅度的提升了 Web 性能。

Tomcat 为了实现支持多种 I/O 模型和应用层协议,一个容器可能对接多个连接器,就好比一个房间有多个门。但是单独的连接器或者容器都不能对外提供服务,需要把它们组装起来才能工作,组装后这个整体叫作 Service 组件。这里请你注意,Service 本身没有做什么重要的事情,只是在连接器和容器外面多包了一层,把它们组装在一起。

Tomcat 内可能有多个 Service,这样的设计也是出于灵活性的考虑。通过在 Tomcat 中配置多个 Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。

image.png

最顶层是 Server,这里的 Server 指的就是一个 Tomcat 实例。一个 Server 中有一个或者多个 Service,多个service主要还是隔离,多个service运行多个应用,互不影响。一个 Service 中有多个连接器和一个容器。连接器与容器之间通过标准的 ServletRequest 和 ServletResponse 通信。

连接器对 Servlet 容器屏蔽了协议及 I/O 模型等的区别,无论是 HTTP 还是 AJP,在容器中获取到的都是一个标准的 ServletRequest 对象。

image.png

Processor 是一个接口,定义了请求的处理等方法。它的抽象实现类 AbstractProcessor 对一些协议共有的属性进行封装,没有对方法进行实现。具体的实现有 AjpProcessor、Http11Processor 等,这些具体实现类实现了特定协议的解析方法和请求处理方式。

image.png

Adapter 组件
客户端发过来的请求信息也不尽相同,Tomcat 定义了自己的 Request 类来“存放”这些请求信息。ProtocolHandler 接口负责解析请求并生成 Tomcat Request 类。但是这个 Request 对象不是标准的 ServletRequest,也就意味着,不能用 Tomcat Request 作为参数来调用容器。Tomcat 设计者的解决方案是引入 CoyoteAdapter,这是适配器模式的经典运用,连接器调用 CoyoteAdapter 的 sevice 方法,传入的是 Tomcat Request 对象,CoyoteAdapter 负责将 Tomcat Request 转成 ServletRequest,再调用容器的 service 方法。

Tomcat & Netty

同步/异步 & 阻塞/非阻塞

I/O是外部设备和主存之间拷贝数据的过程,I/O模型是实现这个过程的方式,后面会详细介绍各种I/O模型的区别:同步阻塞,同步非阻塞、异步等等。

阻塞和非阻塞说的是线程发起IO操作时,如果数据没有就绪,用户进程是否挂起。
同步异步说的是,应用程序与内核交互时,数据从内核空间到用户空间的拷贝,是内核主动发起还是由应用程序来触发 第14讲。