一.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启动的更多相关文章

  1. Tomcat8源码笔记(五)组件Container分析

    Tomcat8源码笔记(四)Server和Service初始化 介绍过Tomcat中Service的初始化 最先初始化就是Container,而Container初始化过程是咋样的? 说到Contai ...

  2. Tomcat8源码笔记(九)组件StandardContext启动流程--未完待续

    StandardContext代表的是webapps下项目,一个项目就是一个StandardContext,作为Tomcat组件的一部分,就会实现Lifecycle接口,被Tomcat管理着生命周期, ...

  3. Tomcat8源码笔记(八)明白Tomcat怎么部署webapps下项目

    以前没想过这么个问题:Tomcat怎么处理webapps下项目,并且我访问浏览器ip: port/项目名/请求路径,以SSM为例,Tomcat怎么就能将请求找到项目呢,项目还是个文件夹类型的? Tom ...

  4. Tomcat8源码笔记(六)连接器Connector分析

    根据 Tomcat8源码笔记(五)组件Container分析 前文分析,StandardService的初始化重心由 StandardEngine转移到了Connector的初始化,本篇记录下Conn ...

  5. Tomcat8源码笔记(四)Server和Service初始化

    上一章 简单说明下Tomcat各个组件: Server:服务器,Tomcat服务器,一个Tomcat只有一个Server组件; Service:业务层,是Server下最大的子容器,一个Server可 ...

  6. Tomcat8源码笔记(三)Catalina加载过程

    之前介绍过 Catalina加载过程是Bootstrap的load调用的  Tomcat8源码笔记(二)Bootstrap启动 按照Catalina的load过程,大致如下: 接下来一步步分析加载过程 ...

  7. Tomcat 启动时 警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context}

    在Eclipse 中,启动Tomcat 时,出现: 警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting proper ...

  8. 警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to

    警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 警告: [SetPro ...

  9. 警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} 解决方法

    Tomcat启动时出现红色警告内容 警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'sour ...

随机推荐

  1. MFC中添加控制台输出

    可以在CWinApp的InitInstance()中调用下面的函数,以生成控制台: #include <io.h> #include <fcntl.h> void InitCo ...

  2. EC20指令测试

    cat /dev/ttyUSB2 & echo -e "AT+CGMM\r\n" >/dev/ttyUSB2   //输出模块型号 echo -e "AT+ ...

  3. mycat跟踪分析

    mycat版本1.6 192.168.5.66 从 192.168.5.67主 一个user表 验证主从 log4j2修改日志level为debug schema.xml配置 启动服务,打开日志tai ...

  4. 牛客网华为机试题之Python解法

    牛客网华为机试题之Python解法 第1题 字符串最后一个单词的长度 a = input().split(" ") print(len(a[-1])) 第2题 计算字符个数 a = ...

  5. 文本超过控件长度自动显示省略号的css

    overflow: hidden; white-space: nowrap; text-overflow: ellipsis;

  6. linux之Ubuntu学习

    开始学习Linux系统是在通过虚拟机VMware上安装Ubuntu操作系统来学习的. 一.Ubuntu安装及使用 第一步:安装虚拟机VMware 第二步:虚拟机安装好之后,创建一个新的虚拟机,安装Ub ...

  7. Leetcode 34 Find First and Last Position of Element in Sorted Array 解题思路 (python)

    本人编程小白,如果有写的不对.或者能更完善的地方请个位批评指正! 这个是leetcode的第34题,这道题的tag是数组,需要用到二分搜索法来解答 34. Find First and Last Po ...

  8. 在linux环境下用中文查询数据库

    1.用SQL在linux环境下,查询语句的中文条件,查不到结果. mysql -h ***.***.***.*** -P 3303 -uroot -p*********** -D boztax -e ...

  9. 刺透内网的HTTP代理

    从偶然出发 在做测试的时候发现了这样一个漏洞,原请求报文如下: GET / HTTP/1.1 Host: attack_website [... HEADER ...] ... 当时最初目的是想测SS ...

  10. web安全学习规划指南

    课程信息 难易程度:中级 学习人数:5078人 已完结:共1节 时长:14分钟 课程介绍 黑客进攻真的会像电影里一样敲几行命令屏幕闪动过后就可以控制目标服务器的么?其实大部分时间黑客是在寻找网站应用的 ...