• 一路远行
    2019-05-23
    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的一个属性。
    展开

    作者回复: 👍

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

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

    
     22
  • why
    2019-05-28
    - 容器的层次结构
        - 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 的一个属性.
    展开

    作者回复: 👍

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

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

    
     14
  • 老王的老李头
    2019-05-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>

     1
     10
  • 发条橙子 。
    2019-05-26
    老师 , 我之前一直误以为一个 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)

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

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

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

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

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

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

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

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

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

    
     5
  • java_lunsong
    2019-07-05
    老师 你好,按照组合的定义,不同的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>
    展开

    作者回复: 对的

    
     3
  • gogo
    2019-06-10
    老师, 关于 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是一个私有成员变量,所以不会有问题。

    
     3
  • -W.LI-
    2019-05-23
    老师好!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应用的目录名。

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

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

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

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

    
     2
  • robert_z_zhang
    2019-06-01
    老师,filter中没有办法直接使用@autowired的方式注入bean,是因为filter是tomcat管理,bean是spring容器管理,filter先于spring bean初始化,但是为什么在filter的init方法中使用applicationContext.getBean的方式就可以获取了呢,是因为init方法调用的时候,spring容器已经完全初始化了吗? init方法调用的时机是什么?

    作者回复: 是的,初始化Filter时,Spring的容器已经初始化好了。

    这个顺序是Tomcat的StandardContext组件的启动方法startInternal决定的,其中关键的三步是:
    1. 调用启动事件监听器ServletContextListener:
    listenerStart() ->这里创建了Spring容器

    2.始化Filter:
    filterStart()

    3.初始化配置了"load on startup"的Servlet
    loadOnStartup()

    
     2
  • z.l
    2019-05-24
    既然tomcat设计上就支持多应用部署,为什么感觉生产环境下都是一个tomcat只部署一个应用,而且到了springboot还把tomcat内置到应用里了。
    
     2
  • 东风第一枝
    2019-05-23
    一直在用Tomcat,现在终于有机会能剥开它仔细看一看了,讲的很棒,醍醐灌顶👍
    
     2
  • Joker
    2019-05-23
    而Spring中的ApplicationContext则负责管理是Spring容器中的bean,和Tomcat的Context的数据有着本质的区别。
    
     2
  • Joker
    2019-05-23
    从具体的数据上来说,Tomcat的Context里面是整个Web应用的参数环境。
    而Servlet规范中的ServletContext接口则既有 Servlet 容器中运行时的参数环境。
    从层级上来说,一个是对应Web应用的,一个则指对应单个Servlet的;一个web应用中可以有多个Servlet。
    ServletContext负责的是servlet运行环境上下信息,不管session管理,不管servlet的加载,他只负责servlet的管理。
    而Tomcat Context需要负责管理session,Servlet的加载和卸载,负责请求信息的处理这些功能。
    展开
    
     2
我们在线,来聊聊吧