前面好几篇都说的是一个请求是怎么到servlet中的service方法的,这一篇我们来看看Tomcat8是怎么启动并且初始化其中的组件的?
相信看了前面几篇的小伙伴应该对Tomcat中的各个组件不陌生了,所以我们就可以加快一点速度;
简单说一下Tomcat启动流程,首先是设置一下各种类加载器,然后加载server.xml配置文件,然后初始化Container各个容器,最后就是连接器Connector。
1.批处理文件
双击startup.bat文件启动tomcat,其实内部就是跳转到catalina.bat文件(.bat文件时windows系统用的,那些.sh配置文件是Unix、Linux系统使用的)
startup.bat文件内部其实就是一些指令从上到下顺序执行,作用是进行一些目录的判断,并找到catalina.bat文件的位置,如下图所示
注意:.bat文件其实就是一个批处理文件,里面一条条的命令都有自己独特的语法,你知需要百度“startup.bat详解”可以看到很多博客,其实就我感觉很像mysql的触发器吧,里面封装了很多命令,有自己独特的语法,只要一触发就会一连串的执行,这里就不多说,不是我们的重点,用到的时候再去仔细研究,这里粗略提一下。
我们再看看catalina.bat文件
由于tomcat是用java语言开发出来的,所以启动Tomcat本质上就是运行一个java程序的main方法,然后后续就会初始化一系列的东西。
2.看看Bootstrap类的main方法
我们就看看main方法内核心部分,在下图中标识了三步:
总结一下,main方法内部核心就是创建一个Bootstrap对象,然后调用这个对象的init、load、start方法,看名字就应该大概猜出一点东西了:
init:大概是初始化一些类加载器什么的;
load:应该是加载什么配置文件,还记得我说过最重要的配置文件是哪个吗?对,就是server.xml文件,其实就是加载这个文件;
start:内部差不多初始化完了所有的容器和连接器,等待连接;
我们分别对这三个方法加断点进行调试,理解其中的原理之后整个Tomcat的启动流程也就清楚了!
3.init方法
还是只看核心的逻辑,至于怎么实例化类加载器和设置类加载器,感兴趣的小伙伴就自己去研究走走源码。
4.load方法
通过看源码,其实可以看到Bootstrap的load方法,就是通过反射调用Catalina的load方法
我们断点进入最后的那个invoke方法,看看Catalina的load方法是加载了些什么东西
这个Digester就是为了解析servler.xml定义了很多的解析规则,真要搞懂这个东西,肯定要花一定的时间去好好研究的;
我们在这里简单而随意看几个我们熟悉的东西,具体的解析过程肯定要自己去了解的;
这个Digester是了解servler.xml具体解析规则必要的东西,我们把规则已经指定好了,下一步肯定就是加载server.xml文件了
准备好了Digester和server.xml的流之后,然后底层用SAX去解析这个xml文件(注意,前面只是解析出来了server.xml中的各个组件对应的类名以及组件之间的父子关系, 没有实例化,到parse方法才真正实例化)
接着我们就要去把一些需要用到的容器进行一些初始化设置,更方便我们使用!
从这里开始我们就会看到一个类似多米诺骨牌效应的这么一个现象,一个接一个的初始化,而且这里涉及到了一个知识点叫做JMX,有关于这个JMX(Java Management Extensions),我查了很多的资料,网上复制粘贴的一大堆,看得很乱,虽然我没有怎么仔细的去研究,但是大概的逻辑我是知道了,下面就简单的说说;
5.JMX的简介和事件监听
便于理解,JMX可以想象成工商局,每家公司必须要向工商局注册自己,这个时候工商局就对每一家公司都有了管理的权利,无论是哪一家公司要改名字,要合并,注销等等,都必须经过工商局的的同意,工商局还能对一些非法公司进行整改什么的,可以说工商局就是管理了每一家公司的生命周期,可以对每家公司进行监控符合管理。
对应在Tomcat中,我们必须要把Tomcat中的所有组件(在这里叫做MBean,类似spring容器中叫做bean)给注册到一个叫做Registry的地方,Registry就是基于JMX搞出来的一个东西,然后我们执行所有组件的init,start方法,都可以在JMX中找到对应的MBean执行对应的init、start方法;这里也只是稍微提一下JMX,内容很多一下子说不完,最好自己多查查资料看看。
JMX的结构图找了好久,终于看到一个图比较满意的,进行一些小小的修改,如下所示:
Agent Layer(代理层):主要包括Agent Services中几个服务(计时器、监控、动态加载MBean、关系服务),还有MBean Server两部分组成
Distributed layer(分布层):包含一些比较厉害的组件,其实我感觉就类似可视化工具,可以很方便的对那些MBean进行一些管理和修改。
还有在MBean Server中的多个MBean组成一个Instrumentation Layer(指示层),这个就没啥说的;总之,JMX我用得不是很多,需要真正用到的时候再慢慢研究吧,哈哈!
感兴趣的小伙伴可以自己研究这个写几个HelloWorldMBean试试就ok了
再说说事件监听,事件监听又是一个什么鬼呢?不准确的来说,在Tomcat启动的时候会有个全局事件(就类似一个全局变量),很多的监听器会时时刻刻监听这个全局事件,当对一个组件进行一些操作(会修改状态码state),那么这个全局事件就会变化,遍历所有监听器执行某方法,下面就随便看看这个全局事件:
还记得最前面的server.xml配置文件的<server>中配置的很多监听器吗?其实就是配合这个全局事件起作用的(看英文应该翻译成生命周期事件吧。。。)
6.接第4步继续
这里一定要理清楚一个逻辑,Tomcat中所有组件都必须是LifecycleBase的子类(其实都直接或者间接的继承了LifecycleBase这个类),这个类就是所有组件的爸爸,假如这个爸爸有九个儿子,大儿子要给最小的儿子一颗糖,你就会看到一个很有趣很坑爹的现象:大儿子-------->爸爸---------->二儿子--------->爸爸--------->三儿子---------->爸爸---------->四儿子---------->.......------->爸爸------->最小的儿子!
这样糖才能到最小的儿子手里,是不是真的很坑爹,哈哈!
所以我们在Tomcat也可以看到一个这样的逻辑:
server.init()---------->LifecycleBase.init(), 在init()方法内部就会执行initInternal()方法,并改变全局事件------------->server中的initInternal()方法 ,内部执行service.init()方法------------>LifecycleBase.init(),执行initInternal()方法,并改变全局事件--------->service中的initInternal()方法,内部就是connector.init()和engine.init()---------->LifecycleBase.init()。。。。。后面都是这个逻辑,几乎一模一样,初始化之后启动start()的逻辑也是这个,所以这个逻辑一定要搞清楚,搞清楚这个了后面就没难度了。
下面我们就简单看看一些关键的地方,由于绝大部分都是相同的,就不一一截图了;
看看initInternal()方法内部:
一直往下看,我们看看StandardService中的initInternal()方法:
engine的初始化是在看不出来什么有趣的东西,我们就重点看看HTTP连接器的初始化的关键代码:
【省略很多重复步骤】
【省略很多重复步骤】
这个是Tomcat的NIO连接,其中addr=0.0.0.0/0.0.0.0:8080;acceptCount = 100;看这个就知道监听8080端口,默认并发连接数100个,这个可以自己根据实际情况设置
到这里Tomcat启动初始化的步骤算是结束了,内容看起来比较多,但是思路自我感觉还是比较清晰的,了解JMX和事件监听的相关知识之后,还是比较容易的,不过强调一点,一定要自己走走源码,加深理解!
这篇说的东西不多,就是初始化了几乎所有组件,就好像一个厨师,把所有的菜都洗好并且切好、分类、分盘,调料也准备就绪,下一步,就是开始做出美味的菜肴了。
下一篇我们简单看看Tomcat的start过程。