专栏名称: 预流
敲代码的
目录
相关文章推荐
英国大家谈  ·  19天后实施!英国移民费用大涨价,涨幅最高达 ... ·  16 小时前  
英国大家谈  ·  什么是信托? ·  昨天  
51好读  ›  专栏  ›  预流

Tomcat 7 中 web 应用加载原理(一)Context 构建

预流  · 掘金  ·  · 2018-02-06 10:24

正文

为什么关心 Tomcat 中一个 web 应用的加载过程?在前面的文章中看过多次 Tomcat 的组件结构图,这里再贴出来回顾一下:

之前的 Tomcat 7 启动分析 系列文章中看到 Tomcat 启动的时候将会解析 server.xml,根据里面所配置的各个节点信息逐一初始化和启动相应组件(即分别调用它们的 init 和 start 方法),但浏览一下 Tomcat 7 源码中的 server.xml 的内容,里面对应上图中的已经默认配置的各级组件包括 Server、Service、Engine、Connector、Host、Valve 。上图中的 Context 组件实际就是我们通常所说的一个 web 应用,有趣的是在 server.xml 中并没有配置该组件,而我们默认启动的时候实际上已经有好几个 web 应用可以访问了: 这个到底是怎么回事?

看过前面的 Tomcat 7 的一次请求分析 系列文章的人应该知道,浏览器一次请求送到 Tomcat 服务器之后,最终会根据浏览器中的 url 路径找到相应的实际要访问的 web 应用的 context 对象(默认即 org.apache.catalina.core.StandardContext 类的实例)。比如访问的 url 为 http://localhost:8080/ ,那么将会送到上图 ROOT 文件夹表示的 web 应用中,访问的 url 为 http://localhost:8080/docs ,那么将访问 docs 文件夹表示的 web 应用。所以能够猜到的是在 Tomcat 启动完成后,必定容器内部已经构造好了表示相应web应用的各个 context 对象。

本文就对这个问题一探究竟。在 Tomcat 7 服务器关闭原理 的开头提到,默认的配置下 Tomcat 启动完之后会看到后台实际上总共有 6 个线程在运行:

前面的几篇文章中涉及了 main http-bio-8080-Acceptor-0 http-bio-8080-AsyncTimeout ajp-bio-8009-Acceptor-0 ajp-bio-8009-AsyncTimeout ,已经谈到了这些线程的作用,它们是如何产生并响应请求的。但有一个线程没有说,即 ContainerBackgroundProcessor[StandardEngine[Catalina]] ,而本文要解答的问题奥秘就在这个线程之中。

先看看这个线程是如何产生的,其实从命名就可以看出一些端倪,它叫做容器后台处理器,并且跟 StandardEngine 关联起来,它的产生于作用也同样如此。

Tomcat 7 中所有的默认容器组件( StandardEngine、StandardHost、StandardContext、StandardWrapper )都会继承父类 org.apache.catalina.core.ContainerBase ,在这些容器组件启动时将会调用自己内部的 startInternal 方法,在该方法内部一般会调用父类的 startInternal 方法( StandardContext 类的实现除外),比如 org.apache.catalina.core.StandardEngine 类中的 startInternal 方法:

最后的 super.startInternal() 即调用父类 org.apache.catalina.core.ContainerBase的startInternal 方法,在该方法最后:
第 6 行设置了 LifecycleState.STARTING 状态(这样将向容器发布一个 Lifecycle.START_EVENT 事件),这一行的作用本文后面会提到,暂且按下不表。第 9 行调用 threadStart 方法,看看 threadStart 方法的代码:
这里可以看到如果两个前置校验条件通过的话将会启动一个线程,并且线程的名字即以 ContainerBackgroundProcessor[ 开头,线程名字后面取的是对象的 toString 方法,以 StandardEngine 为例,看看 org.apache.catalina.core.StandardEngine 的 toString 方法实现:
以上解释了这个后台线程的来历。

但这里有一个问题,既然 StandardEngine、StandardHost 都会调用 super.startInternal() 方法,按默认配置,后台理应产生两个后台线程,实际为什么只有一个?

回到 org.apache.catalina.core.ContainerBase 的 threadStart 方法,在启动线程代码之前有两个校验条件:

容器组件对象初始化时 thread 为 null ,backgroundProcessorDelay 是 -1
org.apache.catalina.core.StandardEngine 在其自身构造函数中做了一点修改:
构造函数最后将父类的 backgroundProcessorDelay 的值由 -1 改成了 10 ,所以 Tomcat 启动解析 xml 时碰到一个 Engine 节点就会对应产生一个后台处理线程。







请到「今天看啥」查看全文