06 | Tomcat系统架构(下):聊聊多层容器的设计
该思维导图由 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-239221 - 阿旺你好 请问到业务的controller是从哪部分进去的呢 谢谢
作者回复: Wrapper -> Filter -> DispatcherServlet -> Controller
2019-05-23473 - 发条橙子 。老师 , 我之前一直误以为一个 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-26469 - 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-28459 - yhhBasic value 有些疑惑。比如engine容器下有多个host容器,那engine容器的basic value是怎么知道要指向哪个host容器的value呢?
作者回复: 好问题,Mapper组件在映射请求的时候,会在Request对象中存储相应的Host、Context等对象,这些选定的容器用来处理这个特定的请求,因此Engine中的Valve是从Request对象拿到Host容器的。
2019-05-24344 - 永光老师您好, 你看这样理解对吗?,一个Engine可以对应多个Host,一个Host下面可以放多个应用。 问题: 1、如果我有两个应用需要部署,现在就可以有很多种方案了。 部署在同一个service下(同一个Engine)的同一个Host目录下 部署在同一个service下(同一个Engine)的不同Host目录下 部署在不同Service下(不同Engine)的各自Host目录下 这几种部署方式有什么差异以及优缺点?以及分别适用于什么场合呀? 谢谢
作者回复: 如果你需要隔离访问域名,用第二种;如果要进一步隔离访问端口,用第三种。
2019-06-04327 - 而立斋老师,能提供一份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-27324 - 天天向上Connector和Engine是平级的,并且 Connector可以有多个 容器结构图 xml结构表示的有问题吧
作者回复: 是的,谢谢指出,已经在紧急修复了,正确的关系是这样的: <Server> <Service> <Connector> </Connector> <Engine> <Host> <Context> </Context> </Host> </Engine> </Service> </Server>
2019-05-2318 - 一路远行这段话的描述有些不准确: “首先,根据协议和端口号选定 Service 和 Engine。” 我的理解是,connector配置中只要有端口号就可以确定service和engine,协议的配置只是为了解析,对请求的路由决策没有起到作用。
作者回复: 你理解对了,这段话的下面就有解释
2019-05-2316 - 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-1015