2019独角兽企业重金招聘Python工程师标准>>>

微信公众号「后端进阶」,专注后端技术分享:Java、Golang、WEB框架、分布式中间件、服务治理等等。
老司机倾囊相授,带你一路进阶,来不及解释了快上车!

Tomcat 是 Java WEB 开发接触最多的 Servlet 容器,但它不仅仅是一个 Servlet 容器,它还是一个 WEB 应用服务器,在微服务架构体系下,为了降低部署成本,减少资源的开销,追求的是轻量化与稳定,而 Tomcat 是一个轻量级应用服务器,自然被很多开发人员所接受。

Tomcat 里面藏着很多值得我们每个 Java WEB 开发者学习的知识,可以这么说,当你弄懂了 Tomcat 的设计原理,Java WEB 开发对你来说已经没有什么秘密可言了。本篇文章主要是跟大家聊聊 Tomcat 的内部架构体系,让大家对 Tomcat 有个整体的认知。

前面我也说了,Tomcat 的本质其实就是一个 WEB 服务器 + 一个 Servlet 容器,那么它必然需要处理网络的连接与 Servlet 的管理,因此,Tomcat 设计了两个核心组件来实现这两个功能,分别是连接器和容器,连接器用来处理外部网络连接,容器用来处理内部 Servlet,我用一张图来表示它们的关系:

一个 Tomcat 代表一个 Server 服务器,一个 Server 服务器可以包含多个 Service 服务,Tomcat 默认的 Service 服务是 Catalina,而一个 Service 服务可以包含多个连接器,因为 Tomcat 支持多种网络协议,包括 HTTP/1.1、HTTP/2、AJP 等等,一个 Service 服务还会包括一个容器,容器外部会有一层 Engine 引擎所包裹,负责与处理连接器的请求与响应,连接器与容器之间通过 ServletRequest 和 ServletResponse 对象进行交流。

也可以从 server.xml 的配置结构可以看出 tomcat 整体的内部结构:

<Server port="8005" shutdown="SHUTDOWN"><Service name="Catalina"><Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/><Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/><Engine defaultHost="localhost" name="Catalina"><Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true"><Context docBase="handler-api" path="/handler" reloadable="true" source="org.eclipse.jst.jee.server:handler-api"/></Host></Engine></Service>
</Server>

连接器(Connector)

连接器负责将各种网络协议封装起来,对外部屏蔽了网络连接与 IO 处理的细节,将处理得到的 Request 对象传递给容器处理,Tomcat 将处理请求的细节封装到 ProtocolHandler,ProtocolHandler 是一个接口类型,通过实现 ProtocolHandler 来实现各种协议的处理,如 Http11AprProtocol:

ProtocolHandler 采用组件模式的设计,将处理网络连接,字节流封装成 Request 对象,再将 Request 适配成 Servlet 处理 ServletRequest 对象这几个动作,用组件封装起来了,ProtocolHandler 包括了三个组件:Endpoint、Processor、Adapter。

Endpoint 在 ProtocolHandler 实现类的构造方法中创建,如下:

public Http11AprProtocol() {super(new AprEndpoint());
}

Endpoint 组件用来处理底层的 Socket 网络连接,AprEndpoint 里面有个叫 SocketProcessor 的内部类,它负责为 AprEndpoint 将接收到的 Socket 请求转化成 Request 对象,SocketProcessor 实现了 Runnable 接口,它会有一个专门的线程池来处理,后面我会单独从源码的角度分析 Endpoint 组件的设计原理。

org.apache.tomcat.util.net.AprEndpoint.SocketProcessor#doRun:

// Process the request from this socket
SocketState state = getHandler().process(socketWrapper, event);

process 方法会创建一个 processor 对象,调用它的 process 方法将 Socket 字节流封装成 Request 对象,在创建 Processor 组件时,会将 Adapter 组件添加到 Processor 组件中:

org.apache.coyote.http11.AbstractHttp11Protocol#createProcessor:

protected Processor createProcessor() {Http11Processor processor = new Http11Processor();// set Adapter 组件processor.setAdapter(getAdapter());return processor;
}

而 Adapter 组件在连接器初始化时就已经创建好了:

org.apache.catalina.connector.Connector#initInternal:

// Initialize adapter
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);

目前为止,Tomcat 只有一个 Adapter 实现类,就是 CoyoteAdapter。Adapter 的主要作用是将 Request 对象适配成容器能够识别的 Request 对象,比如 Servlet 容器,它的只能识别 ServletRequest 对象,这时候就需要 Adapter 适配器类作一层适配。

以上连接器的各个组件,我用一张图说明它们直接的关系:

容器(Container)

在 Tomcat 中一共设计了 4 种容器,它们分别为 Engine、Host、Context、Wrapper,它们的关系如下图所示:

  • Engine:表示一个虚拟主机的引擎,一个 Tomcat Server 只有一个 引擎,连接器所有的请求都交给引擎处理,而引擎则会交给相应的虚拟主机去处理请求;
  • Host:表示虚拟主机,一个容器可以有多个虚拟主机,每个主机都有对应的域名,在 Tomcat 中,一个 webapps 就代表一个虚拟主机,当然 webapps 可以配置多个;
  • Context:表示一个应用容器,一个虚拟主机可以拥有多个应用,webapps 中每个目录都代表一个 Context,每个应用可以配置多个 Servlet。

从上图可看出,各个容器组件之间的关系是由大到小,即父子关系,它们之间关系形成一个树状的结构,它们的实现类都实现了 Container 接口,它有如下方法来控制容器组件之间的关系:

public interface Container extends Lifecycle {Container getParent();void setParent(Container container);void addChild(Container child);Container findChild(String name);Container[] findChildren();void removeChild(Container child);
}

容器组件之间通过以上几个方法,即可实现它们之间的父子关系,有没有发现,Container 接口还继承了 Lifecycle 接口,它有如下方法:

public interface Lifecycle {   public static final String INIT_EVENT = "init";  public static final String START_EVENT = "start";   public static final String BEFORE_START_EVENT = "before_start";   public static final String AFTER_START_EVENT = "after_start";   public static final String STOP_EVENT = "stop";   public static final String BEFORE_STOP_EVENT = "before_stop";   public static final String AFTER_STOP_EVENT = "after_stop";   public static final String DESTROY_EVENT = "destroy";  public void addLifecycleListener(LifecycleListener listener);   public LifecycleListener[] findLifecycleListeners();   public void removeLifecycleListener(LifecycleListener listener);   public void start() throws LifecycleException;   public void stop() throws LifecycleException;
}

Tomcat 中有很多组件,组件通过实现 Lifecycle 接口,Tomcat 通过事件机制来实现对这些组件生命周期的管理。

Tomcat 的这种容器设计思想,其实是运用了组合设计模式的思想,组合设计模式最大的优点是可以自由添加节点,这样也就使得 Tomcat 的容器组件非常地容易进行扩展,符合设计模式中的开闭原则。

现在我们知道了 Tomcat 的容器组件的组合方式,那我们现在就来想一个问题:

当一个请求过来时,Tomcat 是如何识别请求并将它交给特定 Servlet 来处理呢?

从容器的组合关系可以看出,它们调用顺序必定是:

Engine -> Host -> Context -> Wrapper -> Servlet

那么 Tomcat 是如何来定位 Servlet 的呢?答案是利用 Mapper 组件来完成定位的工作。

Mapper 最主要的核心功能是保存容器组件之间访问路径的映射关系,它是如何做到这点的呢?

我们不妨先从源码入手:

org.apache.catalina.core.StandardService:

protected final Mapper mapper = new Mapper();
protected final MapperListener mapperListener = new MapperListener(this);

Service 实现类中,已经初始化了 Mapper 组件以及它的监听类 MapperListener,这里先说明一下,在 Tomcat 组件中,标准的实现组件类前缀会有 Standard,比如:

org.apache.catalina.core.StandardServer
org.apache.catalina.core.StandardService
org.apache.catalina.core.StandardEngine
org.apache.catalina.core.StandardHost
org.apache.catalina.core.StandardContext
org.apache.catalina.core.StandardWrapperz

在 Service 服务启动的时候,会调用 MapperListener.start() 方法,最终会执行 MapperListener 的 startInternal 方法:

org.apache.catalina.mapper.MapperListener#startInternal:

Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {Host host = (Host) conHost;if (!LifecycleState.NEW.equals(host.getState())) {// Registering the host will register the context and wrappersregisterHost(host);}
}

该方法会注册新的虚拟主机,接着 registerHost() 方法会注册 context,以此类推,从而将容器组件直接的访问的路径都注册到 Mapper 中。

定位 Servlet 的流程图:

转载于:https://my.oschina.net/objcoding/blog/3055999

聊聊Tomcat的架构设计相关推荐

  1. 初探Tomcat的架构设计

    Tomcat 作为 servlet 容器实现,它是基于 Java 语言开发的轻量级应用服务器.因为 Tomcat 作为应用服务器,它有着完全开源,轻量,性能稳定,部署成本低等优点,所以它成为目前 Ja ...

  2. 我对支付平台架构设计的一些思考

    我在前一家公司的第一个任务是开发统一支付平台,由于公司的业务需求,需要接入多个第三方支付,之前公司的支付都是散落在各个项目中,及其不利于支付的管理,于是聚合三方支付,统一支付平台的任务就落在我手上,可 ...

  3. Tomcat 架构原理解析到架构设计借鉴

    ‍ 点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 Tomcat 架构原理解析到架构设计借鉴 Tomcat 发展这 ...

  4. mysql性能调优与架构设计_了解架构设计远远不够!一文拆解 Tomcat 高并发原理与性能调优

    来源 | 码哥字节 上帝视角拆解 Tomcat 架构设计,在了解整个组件设计思路之后.我们需要下凡深入了解每个组件的细节实现.从远到近,架构给人以宏观思维,细节展现饱满的美.关注「码哥字节」获取更多硬 ...

  5. Tomcat 架构原理到架构设计,写得非常好!

    Tomcat 架构原理解析到架构设计借鉴 Tomcat 发展这么多年,已经比较成熟稳定.在如今『追新求快』的时代,Tomcat 作为 Java Web 开发必备的工具似乎变成了『熟悉的陌生人』,难道说 ...

  6. 了解架构设计远远不够!一文拆解 Tomcat 高并发原理与性能调优

    来源 | 码哥字节 上帝视角拆解 Tomcat 架构设计,在了解整个组件设计思路之后.我们需要下凡深入了解每个组件的细节实现.从远到近,架构给人以宏观思维,细节展现饱满的美.关注「码哥字节」获取更多硬 ...

  7. 【Tomcat 】Tomcat 架构原理解析到架构设计借鉴

    Tomcat 发展这么多年,已经比较成熟稳定.在如今『追新求快』的时代,Tomcat 作为 Java Web 开发必备的工具似乎变成了『熟悉的陌生人』,难道说如今就没有必要深入学习它了么?学习它我们又 ...

  8. Nginx及其架构设计

    1.1. 什么是 Nginx Nginx 是俄罗斯人编写的十分轻量级的 HTTP 服务器,Nginx,它的发音为"engine X",是一个高性能的HTTP和反向代理服务器,同时也 ...

  9. 聊聊微服务架构中的多级缓存设计

    大家好,我是不才陈某~ 今天我们来聊聊缓存这个话题,看看在微服务环境下如何设计有效的多级缓存架构.主要涉及三方面内容: Web 应用的客户端缓存: 应用层静态资源缓存: 服务层多级缓存. 首先,咱们先 ...

最新文章

  1. Exception in thread main java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
  2. Spring boot配置Servlet容器
  3. [css] CSS3中的transition是否可以过渡opacity和display?
  4. 2021-08-15
  5. 我是这样分析Linux性能问题的
  6. Windows VS Linux,Linux或将成为最终赢家?
  7. CSDN统计出错:一天访问量竟然有48798
  8. 火山安卓自定义组件封装源码讲解
  9. 移动端UI设计尺寸规范以及iPhone尺寸大小
  10. 三维激光雷达点云匹配最近工作总结
  11. 一篇基金研报--《外包服务:后安迪-比尔时代IT产业的大餐》
  12. FBReader 探究
  13. 彩扩机项目--散热马达驱动部分
  14. call forwarding
  15. 服务器系统获取最高权限,webshell+serv-u获取系统最高权限
  16. JAVA pdf中插入自定义图片
  17. C实现前序遍历二叉树
  18. 开源STM32主控遥控器XBOX外形PCB
  19. 苹果官方iPhone/iPad以旧换新5步攻略
  20. MyBatis出现过时问题

热门文章

  1. Dubbo环境搭建-ZooKeeper注册中心
  2. Winform中使用DevExpress的CheckEdit控件实现多选条件搜索
  3. DataTables中提示:DataTables warning: table id=example - Cannot reinitialise DataTable.
  4. 源代码主干分支开发四大模式
  5. r语言plot函数x轴y轴名字_Matplotlib入门-1-plt.plot( )绘制折线图
  6. java action 上传文件_JavaWeb框架_Struts2_(七)-----文件的上传和下载
  7. 八城联动丨神策 2020 数据驱动用户大会「合肥站」邀您免费参会!
  8. 精彩回顾丨神策 2020 数据驱动用户大会亮点汇总
  9. Citrix XenServer XenCenter 警报
  10. 2013年展望:大数据发展十大趋势分析