深入拆解Tomcat & Jetty
李号双
eBay技术主管
立即订阅
6067 人已学习
课程目录
已完结 44 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | Java程序员如何快速成长?
免费
模块一 必备基础 (4讲)
01 | Web容器学习路径
02 | HTTP协议必知必会
03 | 你应该知道的Servlet规范和Servlet容器
04 | 实战:纯手工打造和运行一个Servlet
模块二 整体架构 (9讲)
05 | Tomcat系统架构(上): 连接器是如何设计的?
06 | Tomcat系统架构(下):聊聊多层容器的设计
07 | Tomcat如何实现一键式启停?
08 | Tomcat的“高层们”都负责做什么?
09 | 比较:Jetty架构特点之Connector组件
10 | 比较:Jetty架构特点之Handler组件
11 | 总结:从Tomcat和Jetty中提炼组件化设计规范
12 | 实战:优化并提高Tomcat启动速度
13 | 热点问题答疑(1):如何学习源码?
模块三 连接器 (9讲)
14 | NioEndpoint组件:Tomcat如何实现非阻塞I/O?
15 | Nio2Endpoint组件:Tomcat如何实现异步I/O?
16 | AprEndpoint组件:Tomcat APR提高I/O性能的秘密
17 | Executor组件:Tomcat如何扩展Java线程池?
18 | 新特性:Tomcat如何支持WebSocket?
19 | 比较:Jetty的线程策略EatWhatYouKill
20 | 总结:Tomcat和Jetty中的对象池技术
21 | 总结:Tomcat和Jetty的高性能、高并发之道
22 | 热点问题答疑(2):内核如何阻塞与唤醒进程?
模块四 容器 (8讲)
23 | Host容器:Tomcat如何实现热部署和热加载?
24 | Context容器(上):Tomcat如何打破双亲委托机制?
25 | Context容器(中):Tomcat如何隔离Web应用?
26 | Context容器(下):Tomcat如何实现Servlet规范?
27 | 新特性:Tomcat如何支持异步Servlet?
28 | 新特性:Spring Boot如何使用内嵌式的Tomcat和Jetty?
29 | 比较:Jetty如何实现具有上下文信息的责任链?
30 | 热点问题答疑(3):Spring框架中的设计模式
模块五 通用组件 (4讲)
31 | Logger组件:Tomcat的日志框架及实战
32 | Manager组件:Tomcat的Session管理机制解析
33 | Cluster组件:Tomcat的集群通信原理
特别放送 | 如何持续保持对学习的兴趣?
模块六 性能优化 (8讲)
34 | JVM GC原理及调优的基本思路
35 | 如何监控Tomcat的性能?
36 | Tomcat I/O和线程池的并发调优
37 | Tomcat内存溢出的原因分析及调优
38 | Tomcat拒绝连接原因分析及网络优化
39 | Tomcat进程占用CPU过高怎么办?
40 | 谈谈Jetty性能调优的思路
41 | 热点问题答疑(4): Tomcat和Jetty有哪些不同?
结束语 (1讲)
结束语 | 静下心来,品味经典
深入拆解Tomcat & Jetty
登录|注册

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

李号双 2019-05-23
专栏上一期我们学完了连接器的设计,今天我们一起来看一下 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/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《深入拆解Tomcat & Jetty 》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(71)

  • 一路远行
    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
    1
    102
  • 阿旺
    你好 请问到业务的controller是从哪部分进去的呢 谢谢

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

    2019-05-23
    20
  • 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
    2
    16
  • yhh
    Basic value 有些疑惑。比如engine容器下有多个host容器,那engine容器的basic value是怎么知道要指向哪个host容器的value呢?

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

    2019-05-24
    13
  • 发条橙子 。
    老师 , 我之前一直误以为一个 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
    1
    10
  • 老王的老李头
    老师,能提供一份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
    1
    9
  • 永光
    老师您好,
    你看这样理解对吗?,一个Engine可以对应多个Host,一个Host下面可以放多个应用。
    问题:
    1、如果我有两个应用需要部署,现在就可以有很多种方案了。
         部署在同一个service下(同一个Engine)的同一个Host目录下
         部署在同一个service下(同一个Engine)的不同Host目录下
         部署在不同Service下(不同Engine)的各自Host目录下
    这几种部署方式有什么差异以及优缺点?以及分别适用于什么场合呀?
    谢谢

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

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

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

    2019-05-23
    7
  • 一路远行
    这段话的描述有些不准确:
    “首先,根据协议和端口号选定 Service 和 Engine。”

    我的理解是,connector配置中只要有端口号就可以确定service和engine,协议的配置只是为了解析,对请求的路由决策没有起到作用。

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

    2019-05-23
    6
  • Monday
    老师,tomcat容器的四层架构是一步一步演化过来的还是一步到位的?如果是演化过来的,是哪些故事推动他们如此演化的?谢谢

    作者回复: Tomca的容器架构是一步到位的,设计之初就考虑到了使用的灵活性,如果说将来有变化,应该会朝着“瘦身”的方向发展。

    2019-05-24
    4
  • java_lunsong
    老师 你好,按照组合的定义,不同的host其实也可以有相同的context。也就是把appbase配置成全部一样的,也是正确的,对吧?
    <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=“webapps” autodeploy=“true” name=“www.domain1.com” unpackwars=“true”></host>
    <host appbase=“webapps” autodeploy=“true” name=“www.domain2.com” unpackwars=“true”></host>
    <host appbase=“webapps” autodeploy=“true” name=“www.domain3.com” unpackwars=“true”></host>
    </engine>
    </service>
    </server>

    作者回复: 对的

    2019-07-05
    3
  • 星辰
    老师, 关于 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
    3
  • a(๑≖ิټ≖ิ)✌
    老师请问像tomcat这种开源项目想要像你这样分析它的源代码的话应该从哪读起呢?有时候想看一些开源项目,不知道该怎么入手研究,打开文件目录从A开头的类翻到Z开头的,也不知道该看哪个

    作者回复: 先看核心类,建议看第13答疑篇

    2019-07-03
    2
  • 代码搬运工
    老师,Pipeline中的getBasic方法没用被用到,Host要调用Host的valve,直接调用request.getHost().getPipeline()().getFirst().invoke()方法。

    作者回复: first valve—valve—valve—basic valve. 最后会调到basic valve

    2019-06-02
    2
  • z.l
    既然tomcat设计上就支持多应用部署,为什么感觉生产环境下都是一个tomcat只部署一个应用,而且到了springboot还把tomcat内置到应用里了。
    2019-05-24
    2
  • kaiux
    一直在用Tomcat,现在终于有机会能剥开它仔细看一看了,讲的很棒,醍醐灌顶👍
    2019-05-23
    2
  • -W.LI-
    老师好!Tomcat,server.xml还是搞不太清楚。最外层是server,一个server对应一个Tomcat实例,server下面可以由很多service组件,service组件是区分协议和端口号(问题1:域名访问的话请求到达这里是已经被解析成port了吗?如果已经解析了后面为啥还能拿到二级域名)。每个service对应多个连接器和一个engine。(问题2:为啥要设置多个连接器对应一个engine,是为了提高连接器接受请求的并发吗?)。engine下面对应host,host区分域名(问题3:不同的二级域名对应多个host?),host下面就是context对应应用通过服务名(webapps下文件夹名区分)(问题4:域名相同路径不同指向不同的服务,是在这配置的么?之前运维同事搞过,原理不太理解),wrapper就是应用程序web.xml中配置的servlet。默认只有一个dispatchservlet。支持对url进行ant风格配置多个。

    作者回复: 1,DNS解析是在客户端完成的,你需要在客户端把两个域名指向同一个IP,可以通过hosts配置,因此只要你使用的端口相同,两个域名其实访问的是同一个Service组件。而Tomcat设计通过请求URL中Host来转发到不同Host组件的。

    2. 不同的连接器代表一种通信的路径,比如同时支持HTTP和HTTPS,不是为了并发。

    3. 对

    4.域名相同指向不同的Web应用,默认情况下你只需要把你的Web应用全部扔到webapps目录下,Tomcat自动检测,创建相应的Context组件,各个web应用的访问路径就是web应用的目录名。

    2019-05-23
    2
  • Joker
    而Spring中的ApplicationContext则负责管理是Spring容器中的bean,和Tomcat的Context的数据有着本质的区别。
    2019-05-23
    2
  • Joker
    从具体的数据上来说,Tomcat的Context里面是整个Web应用的参数环境。
    而Servlet规范中的ServletContext接口则既有 Servlet 容器中运行时的参数环境。
    从层级上来说,一个是对应Web应用的,一个则指对应单个Servlet的;一个web应用中可以有多个Servlet。
    ServletContext负责的是servlet运行环境上下信息,不管session管理,不管servlet的加载,他只负责servlet的管理。
    而Tomcat Context需要负责管理session,Servlet的加载和卸载,负责请求信息的处理这些功能。
    2019-05-23
    2
  • 旋转跳跃俺闭着眼
    老师你好,看了上面的回答我有个疑问,tomcat通过mapper映射定位到对应的servlet容器wrapper,然后执行完valve和filtle后执行dispatcherservlet,再执行controller,意思是不是说,如果我们使用springmvc,那么tomcat中只有一个wrapper容器,就是dispatcherservlet,然后通过dispatcherservlet再映射调用对应controller
    2019-10-14
    1
    1
收起评论
71
返回
顶部