深入拆解 Tomcat & Jetty
李号双
eBay 技术主管
38890 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 45 讲
开篇词 (1讲)
深入拆解 Tomcat & Jetty
15
15
1.0x
00:00/00:00
登录|注册

06 | Tomcat系统架构(下):聊聊多层容器的设计

getFirst
setBasic
getBasic
addValve
invoke
setNext
getNext
findChild
removeChild
addChild
setParent
getParent
Pipeline接口
Valve接口
容器组件与访问路径的映射关系
Container接口
Pipeline-Valve管道
定位过程示例
Mapper组件
组合模式
Wrapper
Context
Host
Engine
Tomcat内的Context组件与Spring中的ApplicationContext的关系
Tomcat内的Context组件与Servlet规范中的ServletContext接口的区别
Valve与Filter的区别
请求在容器中的调用过程
请求定位Servlet的过程
容器的管理
容器的层次结构
课后思考
Tomcat的多层容器设计

该思维导图由 AI 生成,仅供参考

专栏上一期我们学完了连接器的设计,今天我们一起来看一下 Tomcat 的容器设计。先复习一下,上期我讲到了 Tomcat 有两个核心组件:连接器和容器,其中连接器负责外部交流,容器负责内部处理。具体来说就是,连接器处理 Socket 通信和应用层协议的解析,得到 Servlet 请求;而容器则负责处理 Servlet 请求。我们通过下面这张图来回忆一下。
容器,顾名思义就是用来装载东西的器具,在 Tomcat 里,容器就是用来装载 Servlet 的。那 Tomcat 的 Servlet 容器是如何设计的呢?

容器的层次结构

Tomcat 设计了 4 种容器,分别是 Engine、Host、Context 和 Wrapper。这 4 种容器不是平行关系,而是父子关系。下面我画了一张图帮你理解它们的关系。
你可能会问,为什么要设计成这么多层次的容器,这不是增加了复杂度吗?其实这背后的考虑是,Tomcat 通过一种分层的架构,使得 Servlet 容器具有很好的灵活性。
Context 表示一个 Web 应用程序;Wrapper 表示一个 Servlet,一个 Web 应用程序中可能会有多个 Servlet;Host 代表的是一个虚拟主机,或者说一个站点,可以给 Tomcat 配置多个虚拟主机地址,而一个虚拟主机下可以部署多个 Web 应用程序;Engine 表示引擎,用来管理多个虚拟站点,一个 Service 最多只能有一个 Engine。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Tomcat多层容器设计及内部工作原理 Tomcat的多层容器设计为读者提供了深入了解Tomcat内部结构的机会。文章首先介绍了Tomcat的容器设计,包括Engine、Host、Context和Wrapper四种容器的层次结构,以及它们之间的父子关系。通过组合模式管理这些容器,使得Tomcat具有很好的灵活性。接着,文章详细解释了Tomcat如何通过Mapper组件定位请求的Servlet,以及请求定位Servlet的过程。最后,文章介绍了Tomcat中使用的Pipeline-Valve管道和责任链模式,以及Valve和Filter的区别。 Tomcat设计了多层容器是为了灵活性的考虑,灵活性具体体现在一个Tomcat实例(Server)可以有多个Service,每个Service通过多个连接器监听不同的端口,而一个Service又可以支持多个虚拟主机。一个URL网址可以用不同的主机名、不同的端口和不同的路径来访问特定的Servlet实例。 请求的链式调用是基于Pipeline-Valve责任链来完成的,这样的设计使得系统具有良好的可扩展性,如果需要扩展容器本身的功能,只需要增加相应的Valve即可。 总的来说,本文通过深入浅出的方式,生动地介绍了Tomcat的多层容器设计及其内部工作原理,对于想深入了解Tomcat内部结构的读者来说,是一篇值得阅读的文章。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《深入拆解 Tomcat & Jetty 》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(87)

  • 最新
  • 精选
  • 一路远行
    1)Servlet规范中ServletContext表示web应用的上下文环境,而web应用对应tomcat的概念是Context,所以从设计上,ServletContext自然会成为tomcat的Context具体实现的一个成员变量。 2)tomcat内部实现也是这样完成的,ServletContext对应tomcat实现是org.apache.catalina.core.ApplicationContext,Context容器对应tomcat实现是org.apache.catalina.core.StandardContext。ApplicationContext是StandardContext的一个成员变量。 3)Spring的ApplicationContext之前已经介绍过,tomcat启动过程中ContextLoaderListener会监听到容器初始化事件,它的contextInitialized方法中,Spring会初始化全局的Spring根容器ApplicationContext,初始化完毕后,Spring将其存储到ServletContext中。 总而言之,Servlet规范中ServletContext是tomcat的Context实现的一个成员变量,而Spring的ApplicationContext是Servlet规范中ServletContext的一个属性。

    作者回复: 👍

    2019-05-23
    9
    221
  • 阿旺
    你好 请问到业务的controller是从哪部分进去的呢 谢谢

    作者回复: Wrapper -> Filter -> DispatcherServlet -> Controller

    2019-05-23
    4
    73
  • 发条橙子 。
    老师 , 我之前一直误以为一个 server表示我们一个应用(实际上只是代表一个tomcat实例),所以一直不理解为什么 server 下通过 host 可以配置多个应用 ,学了这节,发现是一个context对应一个应用, 自己百度了一下,原来可以通过 host 或者 service 来让 tomcat 访问不同的目录来访问多个应用 。 1. 但是我看生产环境中, 配的都是一个 tomcat 对应一个应用, 多个应用就用多个tomcat 。 那么他和一个 tomcat 加载多个应用有什么区别么 。难道用一个tomcat是为了节约内存么, 用一个tomcat加载多个应用都有什么弊端呢 ?? 比如应用的上限 ,希望老师指点一下 。 而且一个tomcat里多个应用的话, 我们就无法用 ps -ef | grep tomcat 来定位到我们的真实应用了。 2. 老师后面讲的, 通过责任链模式, 一步一步解析到 wrapper 的 servlet , 那不是应该调用servlet 的 doGet()/doPost() 方法了么 ,老师说的创建一个 filter 链,并调用Servlet 的 service 方法, 这句话我不是很理解

    作者回复: 1,你说的对,在同一个Tomcat实例里部署多个Web应用是为了节省内存等资源,不过配置部署有点复杂,应用之间互相影响,加上现在硬件成本将低,多应用部署比较少见了。 2,Servlet接口中定义了service方法,没有doGet/doPost。HttpServlet是一个实现类,实现了service方法,同时留出了doGet/doPost方法让程序员来实现。 你可以通过web.xml配置一个或多个Filter,Servlet容器在调用Servlet的service之前,需要调用这些Filter,于是把这些Filter创建出来,形成链表,依次调用,这个Filter链中的最后一个Filter会负责调用Servlet的service方法。 通过一个调用栈来理解一下: doGet:22, HelloServlet (servlet) service:635, HttpServlet (javax.servlet.http) service:742, HttpServlet (javax.servlet.http) internalDoFilter:231, ApplicationFilterChain (org.apache.catalina.core) doFilter:166, ApplicationFilterChain (org.apache.catalina.core) invoke:199, StandardWrapperValve (org.apache.catalina.core) invoke:96, StandardContextValve (org.apache.catalina.core) invoke:493, AuthenticatorBase (org.apache.catalina.authenticator) invoke:140, StandardHostValve (org.apache.catalina.core) invoke:81, ErrorReportValve (org.apache.catalina.valves) invoke:87, StandardEngineValve (org.apache.catalina.core) service:342, CoyoteAdapter (org.apache.catalina.connector) service:800, Http11Processor (org.apache.coyote.http11) process:66, AbstractProcessorLight (org.apache.coyote) process:806, AbstractProtocol$ConnectionHandler (org.apache.coyote) doRun:1498, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net) run:49, SocketProcessorBase (org.apache.tomcat.util.net) runWorker:1149, ThreadPoolExecutor (java.util.concurrent) run:624, ThreadPoolExecutor$Worker (java.util.concurrent) run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads) run:748, Thread (java.lang)

    2019-05-26
    4
    69
  • why
    - 容器的层次结构 - Engine, Host, Context, Wrapper, 是嵌套关系 - 一个 Tomcat 实例包含一个 Engine, 一个 Engine 包含多个 Host, 以此类推 - 容器采用组合模式, 所有容器组件都实现 Container 接口, 保证使用一致性 - 定位 Servlet 的过程 - 由 Mapper 组件完成, Mapper 组件保存了容器组件与访问路径的映射关系, 根据请求的 URL 进行定位 - 端口号定位出 Service 和 Engine - 域名定位出 Host - URL 路径定位出 Context Web 应用 - URL 路径定位出 Wrapper ( Servlet ) - 在各个层次定位过程中, 都会对请求做一些处理 - 通过 Pipeline-Valve 实现容器间的互相调用 ( 责任链模式 ) - valve 表示一个处理点 ( 如权限认证, 日志等), 处理请求; valve 通过链表串联, 并由 pipeline 维护 - valve 会通过 getNext().invoke() 调用下一个 valve, 最后一个 valve ( Basic ) 调用下一层容器的 pipeline 的第一个 valve - Adapter 调用 Engine pipeline 的第一个 valve - Wrapper 最后一个 valve 会创建一个 Filter 链, 并最终调用 Servlet 的 service 方法 - valve 与 filter 对比 - valve 是 Tomcat 的私有机制, Filter 是 Servlet API 公有标准 - valve 工作在容器级别, 拦截所有应用; Servlet Filter 工作在应用级别, 只能拦截某个应用的请求 - 留言 > Tomcat 的 Context 是一个 Web 应用; Servlet 的 ServletContext 是 Web 应用上下文, 是 Context 的一个成员变量; Spring 的 ApplicationContext 是 spring 容器, 是 Servlet 的一个属性.

    作者回复: 👍

    2019-05-28
    4
    59
  • yhh
    Basic value 有些疑惑。比如engine容器下有多个host容器,那engine容器的basic value是怎么知道要指向哪个host容器的value呢?

    作者回复: 好问题,Mapper组件在映射请求的时候,会在Request对象中存储相应的Host、Context等对象,这些选定的容器用来处理这个特定的请求,因此Engine中的Valve是从Request对象拿到Host容器的。

    2019-05-24
    3
    44
  • 永光
    老师您好, 你看这样理解对吗?,一个Engine可以对应多个Host,一个Host下面可以放多个应用。 问题: 1、如果我有两个应用需要部署,现在就可以有很多种方案了。 部署在同一个service下(同一个Engine)的同一个Host目录下 部署在同一个service下(同一个Engine)的不同Host目录下 部署在不同Service下(不同Engine)的各自Host目录下 这几种部署方式有什么差异以及优缺点?以及分别适用于什么场合呀? 谢谢

    作者回复: 如果你需要隔离访问域名,用第二种;如果要进一步隔离访问端口,用第三种。

    2019-06-04
    3
    27
  • 而立斋
    老师,能提供一份tomcat多host的配置吗?

    作者回复: <server port=“8005” shutdown=“SHUTDOWN”> <service name=“Catalina”> <engine defaulthost=“localhost” name=“Catalina”> <host appbase=“webapps” autodeploy=“true” name=“localhost” unpackwars=“true”></host> <host appbase=“webapps1” autodeploy=“true” name=“www.domain1.com” unpackwars=“true”></host> <host appbase=“webapps2” autodeploy=“true” name=“www.domain2.com” unpackwars=“true”></host> <host appbase=“webapps3” autodeploy=“true” name=“www.domain3.com” unpackwars=“true”></host> </engine> </service> </server>

    2019-05-27
    3
    24
  • 天天向上
    Connector和Engine是平级的,并且 Connector可以有多个 容器结构图 xml结构表示的有问题吧

    作者回复: 是的,谢谢指出,已经在紧急修复了,正确的关系是这样的: <Server> <Service> <Connector> </Connector> <Engine> <Host> <Context> </Context> </Host> </Engine> </Service> </Server>

    2019-05-23
    18
  • 一路远行
    这段话的描述有些不准确: “首先,根据协议和端口号选定 Service 和 Engine。” 我的理解是,connector配置中只要有端口号就可以确定service和engine,协议的配置只是为了解析,对请求的路由决策没有起到作用。

    作者回复: 你理解对了,这段话的下面就有解释

    2019-05-23
    16
  • yang
    老师, 关于 MapperHost的构造方法 public MappedHost(String name, Host host) { super(name, host); realHost = this; contextList = new ContextList(); aliases = new CopyOnWriteArrayList<>(); } 不会存在 this泄露吗? public volatile ContextList contextList; contextList 尽管使用volatile修饰的,可以防止编译指令重拍序,并保证可见性。 但是,它没办法保证 还没构造完,这个new出来的对象,就不被其他线程使用啊? 求老师解答~!

    作者回复: this泄露是指在构造函数里将this赋值给某个变量,而这个变量是外部可见的,这样当另一个线程通过这个变量访问this对象时,this对象还没构造完成,就有问题了。 这里的关键是,this赋值的变量一定是外部可以访问的,比如是个public static成员变量,又或者是外部的一个全局变量,又或者是另一个对象实例的成员变量(比如自己是个监听器,将自己注册到事件源对象中),但是上面代码将this赋值给了realHost,而realHost是一个私有成员变量,所以不会有问题。

    2019-06-10
    15
收起评论
显示
设置
留言
87
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部