vlambda博客
学习文章列表

Tomcat 轻量级容器原理探秘 架构分析篇

Tomcat 轻量级应用服务器原理探秘 架构分析篇
Tomcat 轻量级应用服务器原理探秘 一键停启篇
Tomcat 轻量级应用服务器原理探秘 类加载器篇
Tomcat 轻量级应用服务器原理探秘 异步通信篇

1. 整体架构

Tomcat 是全世界最著名的基于 Java 语言的轻量级应用服务器,是一款完全开源免费的 Servlet 容器实现。同时,它支持 HTML、JS 等静态资源的处理,因此又可以作为轻量级 Web 服务器使用。

Tomcat 作为轻量级 Web 服务器处理连接过来的 Socket 请求有两个功能:

  • 对外 处理连接,将收到的字节流转化为自己想要的 Request 和 Response 对象;
  • 对内 处理 Servlet,将对应的 Request 请求分发到相应的 Servlet 中;

Tomcat 其实就分为两大部分,一部分是 连接器(Connnector)处理对外连接 和 容器(Container)管理对内的 Servlet。

最外层的大框就是代表一个 Tomcat 服务,一个 Tomcat 服务可以对应多个 Service。每个 Service 都有连接器和容器。我们在实际应用的时候都会修改 server.xml 配置文件,这些对应关系其实在配置文件中也能体现,将配置文件简化:

<Server port="8006" shutdown="SHUTDOWN">

  <Service name="Catalina">

    <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
    <Connector port="8010" protocol="AJP/1.3" redirectPort="8443" />

    <Engine name="Catalina" defaultHost="localhost">

      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost"  appBase="webapps">
      </Host>
      
    </Engine>   
  </Service>
</Server>

这里我们可以看到连接器其实就是 Connector,一个 Service 中可以有多个连接器,容器其实对应的就是 Engine。Tomcat 的整体架构简单来说就是这样的对应关系,接下来我们来看连接器的整体架构和容器的整体架构。

2. 连接器组件 Coyote

Coyote 是 Tomcat 中连接器的组件名称 , 是对外的接⼝。客户端通过 Coyote 与服务器建⽴连接、发送请求并接受响应。

我们可以看到上图中连接器传给容器的是 ServletRequest 对象,而容器传给连接器的是 ServletResponse 对象,这些在网络传输过程中是肯定不行的,因为网络传输中传送的字节流。所以连接器的功能需求我们大概能总结出来以下几点:

  1. Socket 连接
  2. 读取请求网络中的字节流
  3. 根据相应的协议 (Http/AJP) 解析字节流,生成统一的 Tomcat Request 对象
  4. 将 Tomcat Request 传给容器
  5. 容器返回 Tomcat Response 对象
  6. 将 Tomcat Response 对象转换为字节流
  7. 将字节流返回给客户端
tomcat连接器.png

其实上面的细分都能总结为以下的三点:

  1. 网络通信
  2. 应用层协议的解析
  3. Tomcat 的 Request/Response 与 ServletRequest/ServletResponse 对象的转化

Tomcat 中它也用了三个类来实现上面的三个功能,分别对应如下:

tomcat连接器组件作用.png
  • EndPoint:EndPoint 是 Coyote 通信端点,即通信监听的接⼝,是具体 Socket 接收和发送处理器,是对传输层的抽象,因此 EndPoint ⽤来实现 TCP/IP 协议的。
  • Processor:Processor 是 Coyote 协议处理接⼝ ,如果说 EndPoint 是⽤来实现 TCP/IP 协议的,那么Processor ⽤来实现 HTTP 协议,Processor 接收来⾃ EndPoint 的 Socket,读取字节流解析成 Tomcat  Request和 Response 对象,并通过 Adapter 将其提交到容器处理,Processor 是对应⽤层协议的抽象。
  • Adapter:由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat 定义了⾃⼰的 Request 类来封装这些请求信息。ProtocolHandler 接⼝负责解析请求并⽣成 Tomcat Request 类。但是这个 Request 对象不是标准的 ServletRequest,不能⽤ Tomcat Request 作为参数来调⽤容器。Tomcat 设计者的解决⽅案是引⼊ CoyoteAdapter,这是适配器模式的经典运⽤,连接器调⽤ CoyoteAdapter 的 Sevice ⽅法,传⼊的是 Tomcat Request 对象,CoyoteAdapter 负责将 Tomcat Request 转成 ServletRequest,再调⽤容器。

3. Servlet容器 Catalina

容器,顾名思义就是装东西的器具,那么这个 Tomcat 容器是装什么的呢?其实主要的就是装了 Servlet 的。那么容器是如何设计的呢?Tomcat 的容器设计其实是用了组合设计模式。

Catalina 是 Tomcat 的 Servlet 容器,Tomcat 本质上就是⼀款 Servlet 容器, 因为 Catalina 才是 Tomcat 的核⼼, 其他模块都是为 Catalina 提供⽀撑的。

我们往往有⼀个认识,Tomcat 就是⼀个 Catalina 的实例,因为 Catalina 是 Tomcat 的核⼼。其实,可以认为整个 Tomcat 就是⼀个 Catalina 实例,Tomcat 启动的时候会初始化这个实例,Catalina 实例通过加载 server.xml 完成其他实例的创建,创建并管理⼀个 Server,Server 创建并管理多个服务,每个服务⼜可以有多个 Connector 和⼀个 Container。

-- ⼀个 Catalina 的实例
    -- ⼀个 Server 的实例
        -- 多个 Service 的实例
            -- 每一个 Service 实例下可以有多个 Connector 实例和一个 Container 实例
tomcat容器结构.png
  • Catalina:负责解析 Tomcat 的配置⽂件(server.xml), 以此来创建服务器 Server 组件并进⾏管理。
  • Server:服务器表示整个 Catalina Servlet 容器以及其它组件,负责组装并启动 Servlaet 引擎,Tomcat连接器。Server 通过实现 Lifecycle 接⼝,提供了⼀种优雅的启动和关闭整个系统的⽅式。
  • Service:服务是 Server 内部的组件,⼀个 Server 包含多个 Service,它将若⼲个 Connector 组件绑定到⼀个 Container。
  • Container:容器,负责处理⽤户的 Servlet 请求,并返回对象给 web ⽤户的模块。

Container 组件的具体结构

容器中有两个模块,一个是顶层模块 Engine,另一个是 Host,其实还有两个模块,一个是 Context 对应的是我们 webapp 里面的每个应用文件夹,每个文件夹就是对应一个 Context,还有一个模块 Wrapper 对应的是我们 Context 中的所有 Servlet,Wrapper 管理了访问关系与具体的 Servlet 的对应。

  • Engine:表示整个 Catalina 的 Servlet 引擎,⽤来管理多个虚拟站点,⼀个 Service 最多只能有⼀个 Engine,但是⼀个引擎可包含多个 Host。
  • Host:代表⼀个虚拟主机,或者说⼀个站点,可以给 Tomcat 配置多个虚拟主机地址,⽽⼀个虚拟主机下可包含多个 Context。
  • Context:表示⼀个 Web 应⽤程序, ⼀个 Web 应⽤可包含多个 Wrapper。
  • Wrapper:表示⼀个 Servlet,Wrapper 作为容器中的最底层,不能包含⼦容器。

4. 请求如何定位

我们就举个最简单的例子,我们本机应用上启动了一个 Tomcat,webapp 下有我们部署的一个应用 baicai。我们在浏览器上输入 http://localhost:8080/baicai/index.do 是如何找到对应 Servlet 进行处理呢?

  1. 请求发送到本机的 8080 端口,被在那里监听的 HTTP/1.1 的连接器 Connector 获得;
  2. 连接器 Connector 将字节流转换为容器所需要的 ServletRequest 对象给同级 Service 下的容器模块 Engine 进行处理;
  3. Engine 获得地址 http://localhost:8080/baicai/index 匹配他下面的 Host 主机
  4. 匹配到名为 localhost 的 Host(就算此时请求为具体的 ip,没有配置相应的 Host,也会交给名为 localhost 的 Host 进行处理,因为他是默认的主机);
  5. Host 匹配到路径为 /baicai 的 Context,即在 webapp 下面找到相应的文件夹;
  6. Context 匹配到 URL 规则为 *.do 的 servlet,对应为某个 Servlet 类;
  7. 调用其 doGet 或者 doPost 方法;
  8. Servlet 执行完以后将对象返回给 Context;
  9. Context 返回给 Host;
  10. Host 返回给 Engine;
  11. Engine 返回给连接器 Connector;
  12. 连接器 Connector 将对象解析为字节流发送给客户端;

Tomcat 轻量级应用服务器原理探秘 架构分析篇
Tomcat 轻量级应用服务器原理探秘 一键停启篇
Tomcat 轻量级应用服务器原理探秘 类加载器篇
Tomcat 轻量级应用服务器原理探秘 异步通信篇