既然 Tomcat 的核心身份乃 Servlet 容器,我们便应更加关注 Servlet 本身。
何谓 Servlet?
追溯至互联网的萌芽时期,Sun 公司(后被 Oracle 纳入麾下)洞察到了这一时代机遇,便推出了 Applet 以支持 Web 应用。然而,Applet 并未如预期般在业界掀起波澜。Sun 在反思之后,意识到机遇与市场前景皆不可辜负,于是决定投身于制定一套新的规范,Servlet 便应运而生。
Servlet,乃 Sun 公司为使 Java 语言能够编织动态且富有交互性的网页,进而迈入 Web 编程之殿堂,所精心制定的一套规范。一个 Servlet 的核心职责可概括为以下三端:
- 构建并充实 Request 对象,囊括 URI、参数、请求方式、头部讯息、以及请求本体等。
- 创建 Response 对象。
- 运行业务逻辑,并将成果经由 Response 的输出流,传递至客户端。
Servlet 自身并不包含 main 方法,故其执行需依托于一容器之中,此容器之存在,正为支撑 Servlet 之功能。Tomcat,便是这样一种 Servlet 容器的具象实现。
整体架构图:
图片
在 Tomcat 的架构图之中,两个核心组件——连接器(Connector)与容器(Container)扮演着心脏般的关键角色,它们的重要性不言而喻。以下是它们各自的职责:
- 连接器(Connector)负责处理与连接相关的事务,它将 Socket 连接转化为 Request 和 Response 对象,以便进行后续处理。
- 容器(Container)则负责封装与管理工作中的 Servlet,并具体承担起处理 Request 请求的重任。
在 Tomcat 的体系结构中,仅存在一个 Server 实例,而一个 Server 可以容纳多个 Service。每个 Service 仅关联一个 Container,但可以配备多个 Connectors。这样的设计意味着一个服务能够通过多个连接器来接纳连接,例如同时提供 HTTP 与 HTTPS 协议的链接,或者针对同一协议的不同端口提供服务。架构的示意图如下(后续将详细介绍 Engine、Host、Context 等组件):
图片
image.png
在 Tomcat 的宏伟蓝图中,多个 Connector 与一个 Container 共同构成了 Service,正是 Service 使得 Tomcat 能够向外提供服务。然而,Service 本身需要一个生存的依托,需要一个能够赋予其生命、掌控其存亡的主宰,这个角色非 Server 莫属。因此,Tomcat 的整个生命周期都受到 Server 的调控。
此外,这些组件之间的包含关系,或者说是层级关系,均可在 Tomcat 配置的心脏——conf目录下的server.xml文件中一览无余。在这个配置文件中,我们可以洞察到 Tomcat 的骨骼架构,理解各个组件如何相互连接、协作,共同支撑起整个服务器的运行。
图片
上边的配置文件,还可以通过下边的一张结构图更清楚的理解:
图片
image.png
在 Tomcat 的架构中,各个组件各司其职,共同织就了一张精密的服务网络。下面,让我们逐一探究这些组件的独特功能:
- Server:作为整个服务器的代表,Server 提供了一种简洁而优雅的方式,用以启动和停止整个 Tomcat 系统。它使得我们无需单独对连接器和容器进行繁琐的启停操作。
- Service:Service 代表着服务本身,一个 Server 可以运行多个 Service。例如,在单个 Tomcat 实例中,可以同时运行订单服务、支付服务和用户服务等。
- Connector:Connector 是连接的桥梁,一个 Service 可以包含多个 Connector,以支持多种通信协议。例如,可以同时支持 AJP、HTTP 和 HTTPS 协议,每种协议都可以通过特定的 Connector 来实现。但是每种协议最终执行的 servlet 是相通的。
- Container:Container 是 Servlet 的栖息之地,它负责管理和执行 Servlet。
- Engine:作为引擎,Engine 是 Container 的最高层级,它可以包含多个 Host。
- Host:Host 代表虚拟主机,每个 Host 可以包含多个 Context。
- Context:Context 是 Web 应用的上下文,它定义了 Web 应用的部署参数和路径。
- Wrapper:Wrapper 是 Servlet 的包装器,它负责 Servlet 的生命周期管理和资源分配。
- Service 服务之下还有各种支撑组件。
Manager:Manager 是会话管理器,它负责管理用户的会话(Session)。
Logger:Logger 是日志管理器,它负责记录和管理日志信息。
Loader:Loader 是类加载器,它与类的加载机制相关,通常由 Context 使用。
Pipeline:Pipeline 是管道组件,它与 Valve 一起实现过滤器功能。
Valve:Valve 是阀门组件,它与 Pipeline 配合,用于实现请求和响应的过滤处理。
Realm:Realm 是安全域,它负责认证和授权。
在 Tomcat 的架构中,除了连接器和容器之外,Pipeline 和 Valve 也扮演着至关重要的角色。它们共同构成了 Tomcat 强大的过滤和处理能力。通过一张图,我们可以更直观地理解这两个组件如何在请求处理流程中发挥作用。
图片
Connector 和 Container 的微妙关系
在 Tomcat 的宏伟舞台之上,当一个请求翩翩而至,它的旅程便开始了:
- Service:请求首先抵达 Service,这是 Tomcat 服务的起点,调度着整个服务的流程。
- Connector:随后,请求被引导至 Connector,它是 Tomcat 的忠实门卫,负责接收来自远方的请求。Connector 将原始的网络连接转化为符合 HTTP 协议的 Request 和 Response 对象。
- Container:Request 和 Response 在 Connector 的精心包装后,便被递交至 Container。它负责管理和执行 Servlet,对请求进行细致的处理。
- 处理与返回:Container 在处理完请求后,将结果交回 Connector。Connector 再次承担起信使的角色,通过 Socket 将处理结果沿着 TCP/IP 协议的路径,送回客户端。
在整个过程中,Connector 不仅是沟通客户端与服务器的桥梁,更是实现 TCP/IP 协议和 HTTP 协议的使者。它确保了请求和响应的准确传递,使得整个 Web 服务顺利的流转起来
Connector 架构分析
连接器(Connector)犹如一扇沟通外界与应用系统的窗口,负责接收来自客户端的请求,将其转化为标准化的 Request 和 Response 对象,并交给容器(Container)进行处理。Container 处理完后再交给 Connector 返回给客户端。从功能上看,我们可以将连接器拆解为以下三个核心环节:
- 请求的捕获: 连接器如何精准地捕捉到来自客户端的海量请求?
- 请求与响应的封装: 连接器是如何将纷繁复杂的原始请求数据,规范地封装成 Request 对象,并将容器处理后的结果打包成 Response 对象的?
- 请求的传递与响应的回传: 封装后的 Request 对象如何被高效地传递给容器,而容器生成的 Response 对象又如何准确地返回给客户端?
我们看下 Connector 的结构图,如下:
图片
Connector 的核心组件与工作原理
Connector 通过 ProtocolHandler 来处理各种类型的网络请求。不同的 ProtocolHandler 对应不同的连接方式,如Http11Protocol使用传统的阻塞式 Socket,而 Http11NioProtocol 则采用高效的非阻塞式 NIO Socket。ProtocolHandler 主要由 Endpoint、Processor 和 Adapter 三个组件构成:
- Endpoint: 负责底层网络连接的管理,包括 Socket 的创建、监听、接受以及连接的关闭。它相当于一个“门卫”,负责把控所有进出系统的网络流量。Endpoint 通常实现了 TCP/IP 协议栈的部分功能,用于建立可靠的网络连接。
- Processor: 专门负责将 Endpoint 接收到的原始 Socket 数据解析为标准化的 HTTP 请求(Request)。它可以看作是一个“翻译官”,将底层的网络字节流转化为应用程序可以理解的请求信息。Processor 实现了 HTTP 协议的具体细节,包括请求行的解析、请求头的处理、请求体的读取等。
- Adapter: 充当了连接器与容器之间的适配器。它将 Processor 处理好的 Request 对象传递给 Container,以便容器中的 Servlet 或其他组件对请求进行具体的处理。Adapter 的作用类似于一个“中介”,将不同层次的组件联系起来。
Endpoint 内部机制Endpoint 的抽象实现 AbstractEndpoint 定义了几个重要的内部类和接口:
- Acceptor: 负责监听来自客户端的连接请求,一旦有新的连接到来,Acceptor 就会创建一个新的 Socket,并将其交给 Handler 处理。
- AsyncTimeout: 用来监控异步请求的超时情况。对于长时间未得到响应的异步请求,AsyncTimeout 会采取相应的处理措施,比如关闭连接或者触发超时事件。
- Handler: 是一个接口,定义了处理新连接的具体逻辑。当 Acceptor 接收到一个新的 Socket 时,会创建一个 Handler 实例,并将其与该 Socket 关联起来。Handler 会调用 Processor 来解析请求,并将处理结果返回给客户端。
小结:
Connector 通过这三个组件的协同工作,实现了从接收客户端请求到返回处理结果的整个过程。Endpoint 负责建立连接,Processor 负责解析请求,Adapter 负责将请求传递给容器。这种分层设计使得 Connector 具有良好的扩展性和可维护性。
Container 如何处理请求的
容器(Container)采用 Pipeline-Valve 管道机制来处理请求,这是一种基于责任链模式的设计。在请求处理过程中,多个 Valve 会依次对请求进行处理,每个 Valve 负责特定的任务。与传统的责任链模式不同,Pipeline-Valve 具有以下特点:
- 固定终点: 每个 Pipeline 都有一个特殊的 Valve,即 BaseValve。BaseValve 位于 Pipeline 的末端,不可删除,确保每个请求都经过它的处理。
- 层级调用: 上层容器的 BaseValve 会调用下层容器的 Pipeline,形成一个嵌套的责任链,使得请求在不同层次的容器中依次得到处理。
Tomcat 中的四个标准容器(Engine、Host、Context、Wrapper)分别对应一个 BaseValve:StandardEngineValve、StandardHostValve、StandardContextValve、StandardWrapperValve。
Pipeline 的处理流程图如下:
图片
当 Connector 接收到一个请求时,它会将请求委派给最顶层的容器——Engine。Engine 拥有一个 Pipeline,就像一条流水线,流水线上的每个工位就是一个 Valve。这些 Valve 按照预定的顺序对请求进行处理。
- Engine 管道: 请求首先进入 Engine 的 Pipeline。在这个管道中,一系列 EngineValve 会依次对请求进行处理,例如进行全局性的日志记录、安全检查等。最后,StandardEngineValve 会将请求转发给下一层容器——Host。
- 逐级传递: Host、Context、Wrapper 等容器都拥有自己的 Pipeline,它们会按照同样的方式处理请求。每个容器的 BaseValve(如 StandardHostValve、StandardContextValve)负责将请求传递给下一层容器。
- FilterChain: 当请求到达 Wrapper 容器时,StandardWrapperValve 会创建一个 FilterChain。FilterChain 包含了与该请求匹配的所有 Filter 和 Servlet。这些 Filter 和 Servlet 会按照配置的顺序依次执行 doFilter 方法,最终调用 Servlet 的 service 方法来处理请求。
- 返回响应: 一旦请求处理完毕,响应结果会沿着原路返回,经过各个 Valve,最终由 Connector 发送回客户端。
好了,我们已经从整体上看到了 Tomcat 的结构,对于每个组件并没有详细分析。后续章节我们会从几个方面来学习 Tomcat