Tomcat架构理解

本文详细阐述了Tomcat的架构组成,包括核心组件如Connector、Container、Engine、Host和Context,以及关键的设计模式如高内聚低耦合、Adapter模式、组合模式和责任链模式。此外,文章还介绍了Tomcat的生命周期管理和组件间的交互过程。
摘要由CSDN通过智能技术生成

目录


Tomcat架构分析
概要

Java Servlet容器(Web服务器):Http服务器+Servlet容器

三种部署路径(server.xml配置):webapps(Host)、绝对路径(Context)、Catalina\localhost(Tomcat9)

核心功能
  1. 处理Socket连接,负责网络字节流与Request和Response对象转化
  2. 加载管理Servlet处理Request
架构组成
  1. Connector:Tomcat的连接器,用于接收请求并将其发送给容器。
  2. Container:Tomcat的容器,负责管理Servlet、JSP和静态资源的生命周期。
  3. Engine:Tomcat的引擎,管理容器的生命周期和分配请求。
  4. Host:Tomcat的主机,可以管理多个Web应用程序。
  5. Context:Tomcat的上下文,用于管理单个Web应用程序的配置信息。
  6. Servlet:Tomcat的Servlet,负责处理请求并生成响应。
  7. JSP:Tomcat的JSP,用于动态生成Web内容。

核心组件

Service -> Connetor绑定端口 -> Engine容器转发 -> Host虚拟主机 -> Context应用 -> Wrapper(Servlet)逻辑处理

Server 组件:指的就是整个 Tomcat 服务器,包含多组服务(Service),负责管理和启动各个Service,同时监听 8005 端口发过来的 shutdown 命令,用于关闭整个容器 。

Service组件:每个 Service 组件都包含了若干用于接收客户端消息的 Connector 组件和处理请求的 Engine 组件。 Service 组件还包含了若干 Executor 组件,每个 Executor 都是一个线程池,它可以为 Service 内所有组件提供线程池执行任务。 Tomcat 内可能有多个 Service,这样的设计也是出于灵活性的考虑。通过在 Tomcat 中配置多个 Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。

连接器Connector组件:Tomcat 与外部世界的连接器,监听固定端口接收外部请求,传递给 Container,并将Container 处理的结果返回给外部。连接器对 Servlet 容器屏蔽了不同的应用层协议及 I/O 模型,无论是 HTTP 还是 AJP,在容器中获取到的都是一个标准的 ServletRequest 对象。

连接器需要实现的功能:

  • 监听网络端口。
  • 接受网络连接请求。
  • 读取请求网络字节流。
  • 根据具体应用层协议(HTTP/AJP)解析字节流,生成统一的 Tomcat Request 对象。
  • 将 Tomcat Request 对象转成标准的 ServletRequest。
  • 调用 Servlet 容器,得到 ServletResponse。
  • 将 ServletResponse 转成 Tomcat Response 对象。
  • 将 Tomcat Response 转成网络字节流。
  • 将响应字节流写回给浏览器。

容器Container组件:容器,顾名思义就是用来装载东西的器具,在 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启动期间会通过解析 server.xml,利用反射创建相应的组件,所以xml中的标签和源码一一对应。

<Server>    //顶层组件,可以包括多个Service
    <Service>  //顶层组件,可包含一个Engine,多个连接器
        <Connector/>//连接器组件,代表通信接口           
        <Engine>//容器组件,一个Engine组件处理Service中的所有请求,包含多个Host
            <Host>  //容器组件,处理特定的Host下客户请求,可包含多个Context
                <Context/>  //容器组件,为特定的Web应用处理所有的客户请求
        </Host>
        </Engine>
    </Service>    
</Server>    
Tomcat设计原则

设计复杂系统的基本思路:

首先要分析需求,根据高内聚低耦合的原则确定子模块,然后找出子模块中的变化点和不变点,用接口和抽象基类去封装不变点,在抽象基类中定义模板方法,让子类自行实现抽象方法,也就是具体子类去实现变化点。

Connector三个功能 - 高内聚低耦合
  • 网络通信。
  • 应用层协议解析。
  • Tomcat Request/Response 与 ServletRequest/ServletResponse 的转化。

三个组件来实现:EndPoint、Processor、Adapter

  • EndPoint 负责提供字节流给 Processor;
  • Processor 负责提供 Tomcat Request 对象给 Adapter;
  • Adapter 负责提供 ServletRequest 对象给容器。
EndPoint

EndPoint 是通信端点,即通信监听的接口,是具体的 Socket 接收和发送处理器,是对传输层的抽象,因此 EndPoint 是用来实现 TCP/IP 协议的。

EndPoint 是一个接口,对应的抽象实现类是 AbstractEndpoint,而 AbstractEndpoint 的具体子类,比如在 NioEndpoint 和 Nio2Endpoint 中,有两个重要的子组件:Acceptor 和 SocketProcessor。其中 Acceptor 用于监听 Socket 连接请求。SocketProcessor 用于处理接收到的 Socket 请求,它实现 Runnable 接口,在 Run 方法里调用协议处理组件 Processor 进行处理。为了提高处理能力,SocketProcessor 被提交到线程池来执行,而这个线程池叫作执行器(Executor)。

Processor

  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 方法。

Adapter - 适配器模式

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

父子容器 - 组合模式

Tomcat 设计了 4 种容器,分别是 Engine、Host、Context、Wrapper

Tomcat 采用组合模式来管理这些容器。具体实现方法是,所有容器组件都实现了 Container 接口,因此组合模式可以使得用户对单容器对象和组合容器对象的使用具有一致性。

Container 接口定义如下:

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);
}
Pipeline-Valve - 责任链模式

连接器中的 Adapter 会调用容器的 Service 方法来执行 Servlet,最先拿到请求的是 Engine 容器,Engine 容器对请求做一些处理后,会把请求传给自己子容器 Host 继续处理,依次类推,最后这个请求会传给 Wrapper 容器,Wrapper 会调用最终的 Servlet 来处理。那么这个调用过程具体是怎么实现的呢?答案是使用 Pipeline-Valve 管道。

Pipeline-Valve 是责任链模式,责任链模式是指在一个请求处理的过程中有很多处理者依次对请求进行处理,每个处理者负责做自己相应的处理,处理完之后将再调用下一个处理者继续处理。

Valve接口设计

理解它的设计,第一步就是阀门设计。Valve 表示一个处理点,比如权限认证和记录日志。

public interface Valve {
    public Valve getNext();
    public void setNext(Valve valve);
    public void invoke(Request request, Response response) throws IOException, ServletException;
}
Pipline接口设计

由于Pipline是为容器设计的,所以它在设计时加入了一个Contained接口, 就是为了制定当前Pipline所属的容器

public interface Pipeline extends Contained {

    // 基础的处理阀
    public Valve getBasic();
    public void setBasic(Valve valve);


    // 对节点(阀门)增删查
    public void addValve(Valve valve);
    public Valve[] getValves();
    public void removeValve(Valve valve);


    // 获取第一个节点,遍历的起点,所以需要有这方法
    public Valve getFirst();


    // 是否所有节点(阀门)都支持处理Servlet3异步处理
    public boolean isAsyncSupported();


    // 找到所有不支持Servlet3异步处理的阀门
    public void findNonAsyncValves(Set<String> result);
}

Pipeline 中维护了 Valve 链表,Valve 可以插入到 Pipeline 中,对请求做某些处理。整个调用链的触发是 Valve 来完成的,Valve 完成自己的处理后,调用 getNext.invoke() 来触发下一个 Valve 调用。每一个容器都有一个 Pipeline 对象,只要触发这个 Pipeline 的第一个 Valve,这个容器里 Pipeline 中的 Valve 就都会被调用到。Basic Valve 处于 Valve 链表的末端,它是 Pipeline 中必不可少的一个 Valve,负责调用下层容器的 Pipeline 里的第一个 Valve。

整个调用过程由连接器中的 Adapter 触发的,它会调用 Engine 的第一个 Valve:

//org.apache.catalina.connector.CoyoteAdapter#service
// Calling the container
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

Wrapper 容器的最后一个 Valve 会创建一个 Filter 链,并调用 doFilter() 方法,最终会调到 Servlet 的 service 方法。

//org.apache.catalina.core.StandardWrapperValve#invoke
filterChain.doFilter(request.getRequest(), response.getResponse());

Valve 和 Filter 的区别:

  • Valve 是 Tomcat 的私有机制,与 Tomcat 的基础架构 /API 是紧耦合的。Servlet API 是公有的标准,所有的 Web 容器包括 Jetty 都支持 Filter 机制。
  •  Valve 工作在 Web 容器级别,拦截所有应用的请求;而 Servlet Filter 工作在应用级别,只能拦截某个 Web 应用的所有请求。
Tomcat生命周期

Tomcat生命周期包含组件的创建、初始化、启动、停止和销毁,通过LifeCycle 接口定义方法:init()、start()、stop() 和 destroy()

public interface Lifecycle {
    /** 第1类:针对监听器 **/
    // 添加监听器
    public void addLifecycleListener(LifecycleListener listener);
    // 获取所有监听器
    public LifecycleListener[] findLifecycleListeners();
    // 移除某个监听器
    public void removeLifecycleListener(LifecycleListener listener);
    
    /** 第2类:针对控制流程 **/
    // 初始化方法
    public void init() throws LifecycleException;
    // 启动方法
    public void start() throws LifecycleException;
    // 停止方法,和start对应
    public void stop() throws LifecycleException;
    // 销毁方法,和init对应
    public void destroy() throws LifecycleException;
    
    /** 第3类:针对状态 **/
    // 获取生命周期状态
    public LifecycleState getState();
    // 获取字符串类型的生命周期状态
    public String getStateName();
}
状态枚举

生命周期类图

StandardServer、StandardService 是 Server 和 Service 组件的具体实现类,它们都继承了 LifeCycleBase。StandardEngine、StandardHost、StandardContext 和 StandardWrapper 是相应容器组件的具体实现类,因为它们都是容器,所以继承了 ContainerBase 抽象基类,而 ContainerBase 实现了 Container 接口,也继承了 LifeCycleBase 类,它们的生命周期管理接口和功能接口是分开的,这也符合设计中接口分离的原则。

  • 32
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值