Tomcat8源码笔记(七)组件启动Server Service Engine Host启动
一.Tomcat启动的入口
Tomcat初始化简单流程前面博客介绍了一遍,组件除了StandardHost都有博客,欢迎大家指文中错误。Tomcat启动类是Bootstrap,而启动容器启动入口位于 Catalina 的start方法: 因为反射调用Bootstrap的Catalina实例的start方法.

Catalina启动分为两个大阶段,一是启动Server(start方法),二是 Server启动之后等待(await方法)

一.StandardServer#start
按照之前记录 Tomcat8源码笔记(一)Lifecycle接口 Tomcat的初始化 init 、启动 start都是采用模板设计模式, 共性的地方就是触发监听的事件,个性的地方就是 initInternal、startInternal ,
StandardServer初始化默认是六个监听器,Tomcat8源码笔记(四)Server和Service初始化 这六个监听器暂且就不记录, 直接查看StandardServer的 startInternal方法.

StandardServer#startInternal解读:首先触发configure_start监听事件,主要是完成NamingContextListener的相关JNDI操作,这个暂且忽略. 设置StandardServer状态进入启动中LifecycleState.STARTING,同样六个触发监听器中相关事件; globalNamingResources同样是Tomcat组件,实现Lifecycle接口,所以globalNamingResources#start也会按照模板方法来,具体干嘛的还不清楚,后面补充; 最核心的地方看到了,StandardServer的初始化交给了Service的start,遍历Server的Service,分别start.
二.StandardService#start
StandardService默认是没有监听器的,所以start模板方法直接看startInternal即可. Tomcat8源码笔记(四)Server和Service初始化 这里介绍过StandardService的初始化,可以证明.
StandardService的startInternal方法按顺序分为四个步骤:启动容器container(通常就是StandardEngine)、启动线程池executor、启动mapperListener、启动connector连接器.
那我们下面就从container, 也就是StandardEngine 启动开始记录.

三. StandardEngine#start
Tomcat8源码笔记(五)组件Container分析 记录过StandardEngine的实例化包括初始化过程,StandardEngine有一个监听器,是Tomcat为我们注册的 EngineConfig.
EngineConfig的lifecycleEvent方法如下: 可以看到EngineConfig的start时机是容器启动staring状态,而StandardEngine一开始启动进入的状态是before_start.

StandardEngine#startInternal如下: 可以看到除了打印日志Starting Servlet Engine以外,就是调用父类的startInternal方法;而StandardEngine的父类是ContainerBase,不是LifecycleMBean这个啥,所以StandardEngine启动核心在ContainerBase中.

ContainerBase#startInternal方法
ContainerBase#startInternal方法,Cluster默认的server.xml中没有配置为空,Realm默认的server.xml中配置了一个LockOutRealm,Realm作为一个组件,start方法又是一遍Tomcat的流程,这里重点不是记这个; StandardEngine的children也就是StandardHost,startStopExecutor在之前介绍的Tomcat8源码笔记(五)组件Container分析 中初始化过了,初始化方法位于ContainerBase#initInternal。 这里可以看到使用线程池执行了StartChild实例,StartChild持有当前StandardHost对象,下面程序会阻塞着,直到StartChild的call执行完毕,程序执行完StartChild,还会调用Pipeline的start,启动自身的后台线程。 startInternal执行逻辑有点多,我们先从StartChild看起!

三.一 StartChild#call
因为StartChild是通过线程池来执行的,我们可以通过IDEA很方便的进行多线程DEBUG,这点我以前用Eclipse时候没发现这种优点!
多线程DEBUG方式,在StartChild#call处打断点,上面Suspend(挂起)地方勾选Thread,具体作用对我而言最明显的就是,有时候多线程DEBUG,不选Thread,线程切换时候IDEA界面在那里抽筋一样多个代码地方跳跃,很影响感受,F6单步调试按一下,屏幕狂跳一下.

切换线程的窗口位于DEBUG视图,下面红框的地方可以选择要调试哪个线程。 记住: StartChild执行的那个线程池出来的线程名字叫Catalina-startStop-,而1代表了线程的编号;另外也可以发现executor.submit(new StartChild())方法,得到的Future,在调用get方法之后会一直堵塞知道方法结束,下图中的main线程就在WAIT等待状态.

跑偏了,可以看到上面StartChild,因为StartChild持有StandardHost实例,call方法也是调用StandardHost#start方法! 而StandardHost组件初始化工作都没有做过,组件状态还是NEW的状态,所以按照Tomcat的start模板方法,会先init初始化,然后再start! StandardHost实例持有一个LifecycleListener :HostConfig,不用说肯定是Tomcat替我们注册的,就像StandardEngine有一个EngineConfig的监听器。
那下面的逻辑清楚了,StandardHost#start,先是触发HostConfig初始化事件监听---->StandardHost#initInternal—>HostConfig初始化结束事件监听---->HostConfig启动前事件监听--->
StandardHost#startInternal---->HostConfig启动结束事件监听.
三.二 HostConfig监听触发事件
可以看到只是给HostConfig设置四个 StandardHost的属性:copyXML、unpackWARs、deployXML、contextClass,另外触发事件只在before_start、start等情况下才有逻辑处理,所以before_init只是设置了上面四个属性而已。

按照上面流程,该是 StandardHost#initInternal:
转念一想,StandardHost继承自ContainerBase,initInternal方法应该也是初始化线程池,StandardEngine初始化的线程池产生的线程名字叫Catalina-startStop-,而StandardHost初始化的线程池产生线程名字类似 localhost-startStop-,此外的super.initInternal肯定就是JMX注册功能了.

继承走流程,该是HostConfig触发初始化结束after_init事件,但是上面有HostConfig触发代码,没有after_init的逻辑处理,所以呢?又重复设置了上面四个属性;
继续流程,HostConfig触发启动之前before_start事件,会调用HostConfig#beforeStart:
HostConfig#beforeStart
host#getCreateDirs默认返回true,host#getAppBaseFile获取到catalina-home/webapps目录,并赋值给StandardHost的appBaseFile,
host#getConfigBaseFile获取到catalina-home/conf/Catalina/localhost目录,并赋值给StandardHost的hostConfigBase,
之后校验这两层目录是否存在、是否是目录,属于校验目录是否存在为下面项目作保障。

继承走流程,StandardHost#startInternal, StandardHost在此之前Pipeline有两个Valve阀门,first是AccessLogValve,basic阀门是StandardHostValve,执行之后会添加一个阀门到first的next,这个阀门就是ErrorReportValve; 此外还会调用父类ContainerBase的startInternal方法. 父类ContainerBase#startInternal方法比较冗长,因为StandardHost没有子容器了,所以startInternal主要从Pipeline的start,到设置组件状态为STARTING,到启动自身守护线程;这里我们重点先关注,设置StandardHost状态为STARTING,这就又触发HostConfig的监听事件了!

HostConfig触发start监听事件,执行的方法是start,以下代码JMX忽略,那就是判断之前的appBaseFile是不是目录,deployOnStartup属性默认true,部署项目在TOMCAT启动的时候,那deployApps肯定就是部署项目了呗!先说好,按照上面流程分析,部署完项目之后呢,HostConfig的start就完事了,StandardHost的startInternal也就剩下启动自身守护线程没记录了,然后最后一步呢触发HostConfig启动完成监听,启动完成也没这种监听啊!所以当下只有deployApps和startThread了!

HostConfig#deployApps
deployApps除了前两行,我觉得每一个方法都是部署项目有关的,就是部署各种各样类型的项目呗!再来回忆杀下,appBase是catalina-home/webapps,configBase是catalina-home/conf/Catalina/localhost,至于这几个路径咋拼成的呢? catalina是StandardEngine的name,localhost是StandardHost的name.

filterAppPaths
unfilteredAppPaths就是webapps下所有文件名集合,说来也巧合我们StandardHost的deployIgnore,没设置过就是null,默认啥都不校验忽略;也告诉我们可以在server.xml中的Host元素里配置deployIgnore=“….”,来完成正则表达式匹配达到不部署某些项目的目的,学到了! 果然一个方法就是一个知识点啊!

deployDescriptors
deployDescriptors方法暂时不知道作用,后续补充!

deployWARs
代码过于长,这里不占用篇幅贴图了,主要适用于部署webapps下的 .war项目,后面会尝试部署一个war项目,暂时留作不记录,代码位置位于 HostConfig#deployWARs !
deployDirectories
这个适合我们分析,因为部署的都是文件夹型的项目,实例化了ContextName类,并且部署项目方式是线程池来执行DeployDirectory,并且会一直阻塞着直到webapps下目录项目都部署完毕,我们又可以使用IDEA多线程方式DEBUG

DeployDirectory类定义如下,我们在run方法中打上断点即可进入DEBUG,另外值得考虑的一点是?万一项目多,为啥Tomcatmore不是采用线程池,扩大一点线程数呢?我们可以看到StandardHost的初始化线程和最大线程数都是一致的,1个? 这是为什么呢?多线程可以加快部署项目速度吗?

在DEBUG之前,在补充一个类ContextName,baseName就是项目文件夹名字,path和name一般都是/文件夹名字,比如 /docs 、 /SpringWeb等等,而Root项目的path是“”,TOMCAT也是根据path来将我们的请求转发到对应项目中。

正式进入项目部署的环节,deployDirectory
代码比较冗长,这里只记录下过程,代码位置在:HostConfig#deployDirectory.
webapps下某个项目为例,假设WEB-INF下不存在content.xml,实例化一个StandardContext,代表一个项目,给StandardContext添加ContextConfig,并且给StandardContext设置ContextName的四个属性值,path、name、baseName、version等等,再将StandardContext加入到StandardHost; 部署完成之后就是存储在HostConfig的deployed中,稍后专门写一篇博客记录部署项目。
Tomcat部署项目打印日志如下:

部署完成webapps下的项目,程序会阻塞着直到项目都部署完成,至此HostConfig#start完成,StandardHost#start还剩下 threadStart方法
由于backgroudProcessorDelay默认设置为-1,所以并没有启动这个ContainerBackgroudProcessor!

至此,StandardHost启动完毕,再次回顾下,StandardHost的启动是通过线程池(线程初始化、最大个数都为1个的池子)来启动的,StandardHost可以说完成了webapps下项目的部署,
StandardEngine启动了StandardHost,创建了一堆StandardContext对应每一个项目,而ContainerBase的initInternal方法还没看完,还剩下三步:管道pipeline启动,设置状态为start触发相应监听,启动自身守护线程,会启动一个ContainerBackgroudProcessor线程!

至此StandardServer包括StandardService的启动完毕,也就意味着是Container的启动完毕,而Tomcat还没启动的就是mapperListener、Connector了,大体记录了一遍流程,篇幅原因还有的细节后面补充.
Tomcat8源码笔记(七)组件启动Server Service Engine Host启动的更多相关文章
- Tomcat8源码笔记(五)组件Container分析
Tomcat8源码笔记(四)Server和Service初始化 介绍过Tomcat中Service的初始化 最先初始化就是Container,而Container初始化过程是咋样的? 说到Contai ...
- Tomcat8源码笔记(九)组件StandardContext启动流程--未完待续
StandardContext代表的是webapps下项目,一个项目就是一个StandardContext,作为Tomcat组件的一部分,就会实现Lifecycle接口,被Tomcat管理着生命周期, ...
- Tomcat8源码笔记(八)明白Tomcat怎么部署webapps下项目
以前没想过这么个问题:Tomcat怎么处理webapps下项目,并且我访问浏览器ip: port/项目名/请求路径,以SSM为例,Tomcat怎么就能将请求找到项目呢,项目还是个文件夹类型的? Tom ...
- Tomcat8源码笔记(六)连接器Connector分析
根据 Tomcat8源码笔记(五)组件Container分析 前文分析,StandardService的初始化重心由 StandardEngine转移到了Connector的初始化,本篇记录下Conn ...
- Tomcat8源码笔记(四)Server和Service初始化
上一章 简单说明下Tomcat各个组件: Server:服务器,Tomcat服务器,一个Tomcat只有一个Server组件; Service:业务层,是Server下最大的子容器,一个Server可 ...
- Tomcat8源码笔记(三)Catalina加载过程
之前介绍过 Catalina加载过程是Bootstrap的load调用的 Tomcat8源码笔记(二)Bootstrap启动 按照Catalina的load过程,大致如下: 接下来一步步分析加载过程 ...
- Tomcat 启动时 警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context}
在Eclipse 中,启动Tomcat 时,出现: 警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting proper ...
- 警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to
警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 警告: [SetPro ...
- 警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} 解决方法
Tomcat启动时出现红色警告内容 警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'sour ...
随机推荐
- wcf生成客户端代理类步骤及语句
通过svcutil.exe工具生成客户端代理类和客户端的配置文件 .在运行中输入cmd打开命令行 ()cd C:\Program Files (x86)\Microsoft SDKs\Windows\ ...
- HTML之 一 标签
一 ,标签分类: 1.普通标签: <h1> hello </h1> hello 2.自闭和标签 <hr/> 二,书写html注意事项 1.标签不能交叉嵌套 2. 标 ...
- java中接口和继承的区别
实际概念区别:区别1:不同的修饰符修饰(interface),(extends)区别2:在面向对象编程中可以有多继承!但是只支持接口的多继承,不支持'继承'的多继承哦而继承在java中具有单根性,子类 ...
- Java Web服务器的联机交易
我们知道服务器可以对外部的请求进行应答 ,在BS架构中,通过浏览器可以向Apache Tomcat或者WebSphere服务器发送请求.但是可能存在请求的渠道不是浏览器的情况,他有可能是另外一个jav ...
- DNS: Internet’s Directory
关于DNS 互联网上几乎一切活动都以DNS请求开始.DNS(Domain Name System)是Internet的目录.访问URL时,设备所要做的第一件事就是询问目录,根据域名查出IP地址. 查询 ...
- idea实现自动sql-generator的使用
1.实现步骤: 9.1 加载插件 <!-- mybatis逆向工程jar包 --> <dependency> <groupId>org.mybatis.gener ...
- Centos7 网络报错Job for iptables.service failed because the control process exited with error code.
今天在进行项目联系的时候,启动在待机的虚拟机,发现虚拟机的网络设置又出现了问题. 我以为像往常一样重启网卡服务就能成功,但是它却报了Job for iptables.service failed be ...
- HTML标签的绝对路径和相对路径
我在javaweb中写json的Demo的时候遇到了这个问题,图片一一直取不出来,查了好久终于解决了,所以现在记录一下. 绝对路径: 其实很容易理解,如果你是一个普通的项目,那就是它在你电脑里真实存在 ...
- cvpr2018(转发一篇头条)
CVPR 2018:腾讯图像去模糊.自动人像操纵最新研究 新智元 2018-05-29 14:13:04 新智元报道 来源:腾讯优图 编辑:江磊.克雷格 [新智元导读]即将在6月美国盐湖城举行的计算机 ...
- 关于 this 关键字的使用
package com.jsti.guiyang_01; /* 自定义Phone类 this关键字 代表当前正在调用这个方法(访问成员变量)的对象(实例) 1.在setxxx方法中用来区分成员变量和局 ...