文档章节

Tomcat源码分析(一)--服务启动

epiclight
 epiclight
发布于 2015/10/09 14:46
字数 2262
阅读 46
收藏 1

 对Tomcat感兴趣是由于《深入理解Tomcat》这本书,之前仅仅是使用到了Tomcat,这本书却让我对Tomcat的实现理解的更加透彻了,在这里希望记录一些自己对Tomcat的理解。由于这本书是基于tomcat4的,所以我的文章也是基于tomcat4的,但是tomcat的核心思想应该是没有变的,最主要的两个组件还是连接器和容器。主要为了学习,就不管是新版本还是旧版本了。

      为了后面的理解,先大致说一下Tomcat的整体架构,Tomcat主要有两个组件,连接器和容器,所谓连接器就是一个http请求过来了,连接器负责接收这个请求,然后转发给容器。容器即servlet容器,容器有很多层,分别是Engine,Host,Context,Wrapper。最大的容器Engine,代表一个servlet引擎,接下来是Host,代表一个虚拟机,然后是Context,代表一个应用,Wrapper对应一个servlet。从连接器传过来连接后,容器便会顺序经过上面的容器,最后到达特定的servlet。要说明的是Engine,Host两种容器在不是必须的。实际上一个简单的tomcat只要连接器和容器就可以了,但tomcat的实现为了统一管理连接器和容器等组件,额外添加了服务器组件(server)和服务组件(service),添加这两个东西的原因我个人觉得就是为了方便统一管理连接器和容器等各种组件。一个server可以有多个service,一个service包含多个连接器和一个容器,当然还有一些其他的东西,看下面的图就很容易理解Tomcat的架构了:


一个父组件又可以包含多个子组件,这些被统一管理的组件都实现了Lifecycle接口。只要一个组件启动了,那么他的所有子组件也会跟着启动,比如一个server启动了,它的所有子service都会跟着启动,service启动了,它的所有连接器和容器等子组件也跟着启动了,这样,tomcat要启动,只要启动server就行了,其他的组件都会跟随着启动,那么server是如何启动的?再让我们从头来看...

  一般启动Tomcat会是运行startup.bat或者startup.sh文件,实际上这两个文件最后会调用org.apache.catalina.startup.Bootstrap类的main方法,这个main方法主要做了两件事情,1:定义和初始化了tomcat自己的类加载器,2:通过反射调用了org.apache.catalina.startup.Catalina的process方法;关键代码如下:

[java] view plaincopyprint?

  1.          ClassLoader commonLoader = null;  

  2.          ClassLoader catalinaLoader = null;  

  3.          ClassLoader sharedLoader = null;  

  4. ..............................初始化以上三个类加载器  

  5.          Class startupClass =catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");  

  6.             Object startupInstance = startupClass.newInstance();  

  7. ......................................  

  8.          methodName = "process";//方法名  

  9.          paramTypes = new Class[1];  

  10.          paramTypes[0] = args.getClass();  

  11.          paramValues = new Object[1]  

  12.          paramValues[0] = args;  

  13.          method =startupInstance.getClass().getMethod(methodName, paramTypes);  

  14.          method.invoke(startupInstance, paramValues);//调用process方法  

org.apache.catalina.startup.Catalina的process方法很短,就是下面一点东西:

[java] view plaincopyprint?

  1. public void process(String args[]) {  

  2.   

  3.      setCatalinaHome();  

  4.      setCatalinaBase();  

  5.      try {  

  6.          if (arguments(args))  

  7.              execute();  

  8.      } catch (Exception e) {  

  9.          e.printStackTrace(System.out);  

  10.      }  

  11.  }  

process的功能也很简单,1:如果catalina.home和catalina.base两个属性没有设置就设置一下,2:参数正确的话就调用execute方法,execute的方法就是简单的调用start方法,其中在判断参数正确的方法arguments中会设置starting标识为true,这样在execute方法中就能调用start方法,start方法是重点,在它里面启动了我们的Tomcat所有的服务,下面是start方法里面一些关键的代码:

[java] view plaincopyprint?

  1.  protected void start() {  

  2.     Digester digester = createStartDigester();  

  3.         File file = configFile();  

  4.         try {  

  5.             InputSource is =  

  6.                 new InputSource("file://" + file.getAbsolutePath());  

  7.             FileInputStream fis = new FileInputStream(file);  

  8.             is.setByteStream(fis);  

  9.             digester.push(this);  

  10.             digester.parse(is);  

  11.             fis.close();  

  12.         } catch (Exception e) {  

  13.             System.out.println("Catalina.start: " + e);  

  14.             e.printStackTrace(System.out);  

  15.             System.exit(1);  

  16.         }  

  17. ........................  

  18.     Thread shutdownHook = new CatalinaShutdownHook();  

  19.   

  20.         // Start the new server  

  21.         if (server instanceof Lifecycle) {  

  22.             try {  

  23.                 server.initialize();  

  24.                 ((Lifecycle) server).start();  

  25.                 try {  

  26.                     // Register shutdown hook  

  27.                     Runtime.getRuntime().addShutdownHook(shutdownHook);  

  28.                 } catch (Throwable t) {  

  29.                     // This will fail on JDK 1.2. Ignoring, as Tomcat can run  

  30.                     // fine without the shutdown hook.  

  31.                 }  

  32.                 // Wait for the server to be told to shut down  

  33.                 server.await();  

  34.             } catch (LifecycleException e) {  

  35.                 System.out.println("Catalina.start: " + e);  

  36.                 e.printStackTrace(System.out);  

  37.                 if (e.getThrowable() != null) {  

  38.                     System.out.println("----- Root Cause -----");  

  39.                     e.getThrowable().printStackTrace(System.out);  

  40.                 }  

  41.             }  

  42.         }  

  43. }  


这里最重要的方法是createStartDigester();和((Lifecycle) server).start();createStartDigester方法主要的作用就是帮我们实例化了所有的服务组件包括server,service和connect,至于怎么实例化的等下再看,start方法就是启动服务实例了。File file = configFile();是新建server.xml文件实例,后面的服务组件都是要根据这个文件来的,现在来看这些服务组件是怎么实例化的,下面是createStartDigester的部分代码:

[java] view plaincopyprint?

  1. protected Digester createStartDigester() {  

  2.   

  3.       // Initialize the digester  

  4.       Digester digester = new Digester();  

  5.       if (debug)  

  6.           digester.setDebug(999);  

  7.       digester.setValidating(false);  

  8.   

  9.       // Configure the actions we will be using  

  10.       digester.addObjectCreate("Server",  

  11.                                "org.apache.catalina.core.StandardServer",  

  12.                                "className");//创建一个对象  

  13.       digester.addSetProperties("Server"); //设置对象的属性  

  14.       digester.addSetNext("Server",  

  15.                           "setServer",  

  16.                           "org.apache.catalina.Server");//创建对象间的关系  

  17.   

  18.       digester.addObjectCreate("Server/GlobalNamingResources",  

  19.                                "org.apache.catalina.deploy.NamingResources");  

  20.       digester.addSetProperties("Server/GlobalNamingResources");  

  21.       digester.addSetNext("Server/GlobalNamingResources",  

  22.                           "setGlobalNamingResources",  

  23.                           "org.apache.catalina.deploy.NamingResources");  

  24.   

  25.       digester.addObjectCreate("Server/Listener",  

  26.                                null// MUST be specified in the element  

  27.                                "className");  

  28.       digester.addSetProperties("Server/Listener");  

  29.       digester.addSetNext("Server/Listener",  

  30.                           "addLifecycleListener",  

  31.                           "org.apache.catalina.LifecycleListener");  

  32.   

  33.       digester.addObjectCreate("Server/Service",  

  34.                                "org.apache.catalina.core.StandardService",  

  35.                                "className");  

  36.       digester.addSetProperties("Server/Service");  

  37.       digester.addSetNext("Server/Service",  

  38.                           "addService",  

  39.                           "org.apache.catalina.Service");  

  40.   

  41.       digester.addObjectCreate("Server/Service/Listener",  

  42.                                null// MUST be specified in the element  

  43.                                "className");  

  44.       digester.addSetProperties("Server/Service/Listener");  

  45.       digester.addSetNext("Server/Service/Listener",  

  46.                           "addLifecycleListener",  

  47.                           "org.apache.catalina.LifecycleListener");  

  48.   

  49.       digester.addObjectCreate("Server/Service/Connector",  

  50.                                "org.apache.catalina.connector.http.HttpConnector",  

  51.                                "className");  

  52.       digester.addSetProperties("Server/Service/Connector");  

  53.       digester.addSetNext("Server/Service/Connector",  

  54.                           "addConnector",  

  55.                           "org.apache.catalina.Connector");  

  56.   

  57.       digester.addObjectCreate("Server/Service/Connector/Factory",  

  58.                                "org.apache.catalina.net.DefaultServerSocketFactory",  

  59.                                "className");  

  60.       digester.addSetProperties("Server/Service/Connector/Factory");  

  61.       digester.addSetNext("Server/Service/Connector/Factory",  

  62.                           "setFactory",  

  63.                           "org.apache.catalina.net.ServerSocketFactory");  

  64.   

  65.       digester.addObjectCreate("Server/Service/Connector/Listener",  

  66.                                null// MUST be specified in the element  

  67.                                "className");  

  68.       digester.addSetProperties("Server/Service/Connector/Listener");  

  69.       digester.addSetNext("Server/Service/Connector/Listener",  

  70.                           "addLifecycleListener",  

  71.                           "org.apache.catalina.LifecycleListener");  

  72.        // Add RuleSets for nested elements  

  73.       digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));  

  74.       digester.addRuleSet(new EngineRuleSet("Server/Service/"));  

  75.       digester.addRuleSet(new HostRuleSet("Server/Service/Engine/")); //有容器和StandardService的关系  

  76.       digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Default"));  

  77.       digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/DefaultContext/"));  

  78.       digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/Default"));  

  79.       digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/DefaultContext/"));  

  80.       digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));  

  81.       digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));  

  82.   

  83.       digester.addRule("Server/Service/Engine",  

  84.                        new SetParentClassLoaderRule(digester,  

  85.                                                     parentClassLoader));  

  86.         

  87.      return (digester);  

  88.   

  89.   }  

关键是红色标注的代码,Digester是一个外部jar包里面的类,主要的功能就是解析xml里面的元素并把元素生成对象,把元素的属性设置成对象的属性,并形成对象间的父子兄弟等关系。digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");//创建一个org.apache.catalina.core.StandardServer对象,实际上这里并没有真正创建出一个对象,而是添加一个模式,只是后面创建的对象是根据这些模式和server.xml来的,所以可以暂时这么理解。真正创建对象是在start方法里面的digester.parse(is),is是server.xml文件的流,digester刚才已经添加了StandardServer和StandardService等服务组件,也添加了StandardServer和StandardService的关系以及StandardService和连接器HttpConnector,容器StandardHost的关系,所以调用digester.parse(is)方法后就会根据模式和server.xml文件来生成对象以及他们之间的相互关系。这样我们便有了服务器组件StandardServer的对象,也有了它的子组件StandardService对象等等,再回到start方法,看下面的代码:

[java] view plaincopyprint?

  1. server.initialize();  

  2. ((Lifecycle) server).start();  

既然有了服务器组件的对象,就初始化然后启动就可以了,到此,tomcat就实现了启动服务器组件StandardServer。启动后做的事情就东西比较多,但是还是比较清晰的,StandardServer的start方法关键代码是启动它的子组件StandardService:

[java] view plaincopyprint?

  1. // Start our defined Services  

  2.        synchronized (services) {  

  3.            for (int i = 0; i < services.length; i++) {  

  4.                if (services[i] instanceof Lifecycle)  

  5.                    ((Lifecycle) services[i]).start();//调用StandardService的start  

  6.            }  

  7.        }  


StandardService的start方法跟StandardServer的start方法差不多,是启动它的连接器和容器,上面说了一个Service包含一个容器和多个连接器:

[java] view plaincopyprint?

  1. // Start our defined Container first  

  2.    if (container != null) {  

  3.        synchronized (container) {  

  4.            if (container instanceof Lifecycle) {  

  5.                ((Lifecycle) container).start();//启动容器  

  6.            }  

  7.        }  

  8.    }  

  9.   

  10.    // Start our defined Connectors second  

  11.    synchronized (connectors) {  

  12.        for (int i = 0; i < connectors.length; i++) {  

  13.            if (connectors[i] instanceof Lifecycle)  

  14.                ((Lifecycle) connectors[i]).start();//启动连接器  

  15.        }  

  16.    }  


默认的连接器是HttpConnector,所以会调用HttpConnector的start方法,它的方法如下:

[java] view plaincopyprint?

  1. public void start() throws LifecycleException {  

  2.   

  3.     // Validate and update our current state  

  4.     if (started)  

  5.         throw new LifecycleException  

  6.             (sm.getString("httpConnector.alreadyStarted"));  

  7.     threadName = "HttpConnector[" + port + "]";  

  8.     lifecycle.fireLifecycleEvent(START_EVENT, null);  

  9.     started = true;  

  10.   

  11.     // Start our background thread  

  12.     threadStart();//启动一个后台线程,用来处理http请求连接  

  13.   

  14.     // Create the specified minimum number of processors  

  15.     while (curProcessors < minProcessors) {  

  16.         if ((maxProcessors > 0) && (curProcessors >= maxProcessors))  

  17.             break;  

  18.         HttpProcessor processor = newProcessor();//后台处理连接的线程  

  19.         recycle(processor);  

  20.     }  

  21.   

  22. }  


这里有个两个关键的类:HttpConnector和HttpProcessor,它们都实现了Runnable接口,HttpConnector负责接收http请求,HttpProcessor负责处理由HttpConnector接收到的请求。注意这里HttpProcessor会有很多的实例,最大可以有maxProcessor个,初始化是20个。所以在threadStart方法中会启动一个后台线程来接收http连接,如下:

[java] view plaincopyprint?

  1. private void threadStart() {  

  2.   

  3.     log(sm.getString("httpConnector.starting"));  

  4.   

  5.     thread = new Thread(this, threadName);  

  6.     thread.setDaemon(true);  

  7.     thread.start();  

  8.   

  9. }  


这样,就会启动HttpConnector后台线程,它的run方法不断循环,主要就是新建一个ServerSocket来监听端口等待连接,主要代码如下:

[java] view plaincopyprint?

  1.  socket = serverSocket.accept();  

  2. ..........................  

  3. ..........................  

  4.  processor.assign(socket);  

serverSocket一直等待连接,得到连接后给HttpProcessor的实例processor来处理,serverSocket则继续循环监听,至于processor具体怎么处理,还有很多要说,这里先不说。

至此,tomcat启动完成了,StandardServer启动后,一直到HttpConnector线程等待监听,就可以处理客户端的http请求了。


本文转载自:http://blog.csdn.net/haitao111313/article/details/7717160

epiclight
粉丝 3
博文 41
码字总数 79
作品 0
深圳
架构师
私信 提问
在Intellij下远程调试tomcat应用(含tomcat源码调试)

环境 服务器: 操作系统:centos6 jdk:1.7 tomcat:8.5.9 本地: 操作系统:mac osx 10.11.6 jdk:1.7 intellij:2016.2 tomcat:8.5.9(本地也需要下载tomcat) 应用调试步骤 java应用远程...

wooyoo
2017/01/23
0
0
Tomcat 源码分析(-)启动过程分析

前面几篇文章分别介绍了Tomcat的安装、优化和框架,本文主要用于分析Tomcat源码启动过程,研究一个框架最好的着手方式可能就是研究它的启动过程,因为在这过程中我们可以看到它内部的层次关系...

AaronSheng
2016/11/28
29
0
Tomcat源码分析

下面谈谈我对Tomcat架构的理解 总体架构: 1、面向组件架构 2、基于JMX 3、事件侦听 1)面向组件架构 tomcat代码看似很庞大,但从结构上看却很清晰和简单,它主要由一堆组件组成,如Server、...

五大三粗
2015/09/28
107
0
TOMCAT源码分析(启动框架)

TOMCAT源码分析(启动框架) 前言: 本文是我阅读了TOMCAT源码后的一些心得。 主要是讲解TOMCAT的系统框架, 以及启动流程。若有错漏之处,敬请批评指教! 建议: 毕竟TOMCAT的框架还是比较复杂...

leamon
2014/03/20
0
0
Tomcat7.0源码分析——类加载体系

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/beliefer/article/details/50995516 前言Tomcat遵循J2EE规范,实现了Web容器。很多有关web的书籍和文章都离不...

泰山不老生
2016/03/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Taro ScrollView 组件的 scrollTop 属性是个坑

官方issue:ScrollView设置scrollTop没效果 同样的,设置 scrollTop=0 并不能实现置顶,官方回复早就修复了,我的 Taro 版本已经是最新的,然而并未修复。 万能的评论区,给出了失效的原因。...

dkvirus
42分钟前
3
0
Qt那些事0.0.21

这次还是关于PRO文件中QMAKE_POST_LINK的故事。 平时都是使用VS2015作为编译器,恰巧想用MinGW编一版程序,结果偏偏出现了错误。话说测试的这个项目可是在Linux下(fodera 20)可以正确编译通...

Ev4n
52分钟前
0
0
OSChina 周六乱弹 —— 抖音外放 亲妈下葬。

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @巴拉迪维 :一直没想明白黎明是怎么混进「四大天王」的,直到最近网易云音乐心动模式开启之后 #今日歌曲推荐# 《那有一天不想你》- 黎明 手机...

小小编辑
今天
447
8
Linux使用源码包安装软件

前言: 最近整理一些以前的学习笔记。 过去都是存储在本地,此次传到网络留待备用。 源码包 Linux软件多数免费、开源,是开发人员编写的,具有很强可读性的一组相关代码文本。 源码包 --> 编...

迷失De挣扎
今天
6
0
IPv4如何转换为IPv6?

ipv6已经逐渐在应用,现在已经有很多的运营商支持ipv6,前天我们也发布了如何让电脑使用ipv6地址?有很多朋友在问?ipv6有什么作用,它的表示方式是什么,今天我们来一起来详细了解下ipv6相关计...

xiangyunyan
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部