知识小罐头08(tomcat8启动源码分析 上)

  前面好几篇都说的是一个请求是怎么到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()方法,并改变全局事件------------->service.init()方法------------>LifecycleBase.init(),执行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过程。

原文地址:https://www.cnblogs.com/wyq1995/p/10165874.html

时间: 2024-07-31 13:40:38

知识小罐头08(tomcat8启动源码分析 上)的相关文章

知识小罐头09(tomcat8启动源码分析 下)

初始化已经完成,现在就是启动这些组件,Tomcat中的start方法就是用于启动的,其实start的原理还是和上一篇说的初始化几乎一样!这里我就大概说一下,看几个比较关键的地方就行了. 前面的步骤就大概截图看一下就ok了 ok,由于前面这些东西基本和初始化的流程一样,跳过,我们就从启动service开始看: 其实关键就是这三步,启动引擎,监听器和连接器,我们好好看看这三步其中的原理. 1.启动引擎 engine.start()方法其实就是到StandardEngine中startInternal

横屏小游戏--萝莉快跑源码分析三

主角出场: 初始化主角 hero = new GameObjHero(); hero->setScale(0.5); hero->setPosition(ccp(100,160)); hero->setVisible(false); addChild(hero,1); 进入GameObjHero类ccp文件 创建主角及动作 this->setContentSize(CCSizeMake(85,90)); //接收触摸事件 CCDirector* pDirector = CCDire

横屏小游戏--萝莉快跑源码分析一

Cpp文件功能介绍 GameAboutScene.cpp 关于页面 GameMainScene.cpp游戏主页面 GameMark.cpp分数 GameMenuScene.cpp游戏主菜单 GameObjHero.cpp主角 GameObjMap.cpp游戏地图 GameObjStar.cpp星星 菜单主页面: 主菜单页面背景 CCSprite* bg = CCSprite::create("MainMenu.png"); bg->setScale(0.5); bg->se

Android之rild进程启动源码分析

Android 电话系统框架介绍 在android系统中rild运行在AP上,AP上的应用通过rild发送AT指令给BP,BP接收到信息后又通过rild传送给AP.AP与BP之间有两种通信方式: 1.Solicited Response:Ap向Bp发送请求,Bp给Ap发送回复,该类型的AT指令及其回调函数以数组的形式存放在Ril_commands.h文件中: {数组中的索引号,请求回调函数,响应回调函数} [plain] view plaincopy {0, NULL, NULL},      

Openstack学习笔记之——Neutron-server服务加载与启动源码分析(三)

本文是在学习Openstack的过程中整理和总结,由于时间和个人能力有限,错误之处在所难免,欢迎指正! 在Neutron-server服务加载与启动源码分析(二)中搞定模块功能的扩展和加载,我们就回到Neutron-server服务加载与启动源码分析(一)中的_run_wsgi函数 <span style="font-size:14px;">def _run_wsgi(app_name): app = config.load_paste_app(app_name) ifno

Django如何启动源码分析

Django如何启动源码分析 启动 我们启动Django是通过python manage.py runsever的命令 解决 这句话就是执行manage.py文件,并在命令行发送一个runsever字符串 解析manage.py #!/usr/bin/env python import os import sys if __name__ == "__main__": #os.environ.setdefault 方法可以修改系统环境变量,但是只能os.environ 只能影响到当前运行

memcached学习笔记——存储命令源码分析上

原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command函数,探究memcached客户端的set命令,解读memcached是如何解析客户端文本命令,剖析memcached的内存管理,LRU算法是如何工作等等. 解析客户端文本命令 客户端向memcached server发出set操作,memcached server读取客户端的命令,客户端的连接状态

axios 源码分析(上) 使用方法

axios是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,它可以在浏览器和node环境下运行,在github上已经有六七万个星了,axios使用很方便,很多人在使用他,vue官方也推荐使用axios了,技术这东西还是随主流吧,大家都用肯定有它的特长所在. axios现在最新的版本的是v0.19.0,实现代码也很好理解.我们本节先说一下它的使用方法,然后来分析一下它的实现源码 我们可以使用两种方式来创建一个axios实例: ·一种是直接调用axios(config) ·

ActivityManagerService服务线程启动源码分析【转】

本文转载自:http://blog.csdn.net/yangwen123/article/details/8177702 Android系统服务线程都驻留在SystemServer进程中,由SystemServer启动,在SystemServer.init2函数中,通过启动一个线程来启动各种系统服务线程. [java] view plain copy public static final void init2() { Slog.i(TAG, "Entered the Android syst