zoukankan      html  css  js  c++  java
  • Tomcat启动过程源码解读

    根据Tomcat源码来看一下Tomcat启动过程都做了什么

    部分代码为主要流程代码,删去了try-catch以及一些校验逻辑,方便理解主流程

    先来一张启动过程时序图,了解一下启动顺序

    Tomcat启动的入口类:org.apache.catalina.startup.Bootstrap#main

    main方法是整个tomcat启动时的入口。在main方法中,使用bootstrap.init()来初始化类加载器和创建Catalina实例,然后再启动Catalina线程。

     1 public static void main(String args[]) {
     2  
     3     if (daemon == null) {
     4         // Don't set daemon until init() has completed
     5         Bootstrap bootstrap = new Bootstrap();
     6         try {
     7             bootstrap.init();
     8         } catch (Throwable t) {
     9             handleThrowable(t);
    10             t.printStackTrace();
    11             return;
    12         }
    13         daemon = bootstrap;
    14     } else {
    15         // When running as a service the call to stop will be on a new
    16         // thread so make sure the correct class loader is used to prevent
    17         // a range of class not found exceptions.
    18         Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
    19     }
    20  
    21     try {
    22         String command = "start";
    23         if (args.length > 0) {
    24             command = args[args.length - 1];
    25         }
    26  
    27         if (command.equals("startd")) {
    28             args[args.length - 1] = "start";
    29             daemon.load(args);
    30             daemon.start();
    31         } else if (command.equals("stopd")) {
    32             args[args.length - 1] = "stop";
    33             daemon.stop();
    34         } else if (command.equals("start")) {
    35             daemon.setAwait(true);
    36             daemon.load(args);
    37             daemon.start();
    38         } else if (command.equals("stop")) {
    39             daemon.stopServer(args);
    40         } else if (command.equals("configtest")) {
    41             daemon.load(args);
    42             if (null==daemon.getServer()) {
    43                 System.exit(1);
    44             }
    45             System.exit(0);
    46         } else {
    47             log.warn("Bootstrap: command "" + command + "" does not exist.");
    48         }
    49     } catch (Throwable t) {
    50         // Unwrap the Exception for clearer error reporting
    51         if (t instanceof InvocationTargetException &&
    52                 t.getCause() != null) {
    53             t = t.getCause();
    54         }
    55         handleThrowable(t);
    56         t.printStackTrace();
    57         System.exit(1);
    58     }
    59  
    60 }

    bootstrap.init()方法,用于初始化容器相关,首先创建类加载器,然后通过反射创建org.apache.catalina.startup.Catalina实例:

     1 public void init() throws Exception {
     2  
     3     initClassLoaders();
     4  
     5     Thread.currentThread().setContextClassLoader(catalinaLoader);
     6  
     7     SecurityClassLoad.securityClassLoad(catalinaLoader);
     8  
     9     // Load our startup class and call its process() method
    10     if (log.isDebugEnabled())
    11         log.debug("Loading startup class");
    12     Class<?> startupClass =
    13         catalinaLoader.loadClass
    14         ("org.apache.catalina.startup.Catalina");
    15     Object startupInstance = startupClass.newInstance();
    16  
    17     // Set the shared extensions class loader
    18     if (log.isDebugEnabled())
    19         log.debug("Setting startup class properties");
    20     String methodName = "setParentClassLoader";
    21     Class<?> paramTypes[] = new Class[1];
    22     paramTypes[0] = Class.forName("java.lang.ClassLoader");
    23     Object paramValues[] = new Object[1];
    24     paramValues[0] = sharedLoader;
    25     Method method =
    26         startupInstance.getClass().getMethod(methodName, paramTypes);
    27     method.invoke(startupInstance, paramValues);
    28  
    29     catalinaDaemon = startupInstance;
    30  
    31 }

    之后Bootstrap的demon.start()方法就会调用Catalina的start方法。

    Catalina实例执行start方法。这里有两个点,一个是load()加载server.xml配置、初始化Server的过程,一个是getServer().start()开启服务、初始化并开启一系列组件、子容器的过程。

    org.apache.catalina.startup.Catalina#start
     1 public void start() {
     2  
     3     if (getServer() == null) {
     4         load();
     5     }
     6  
     7     if (getServer() == null) {
     8         log.fatal("Cannot start server. Server instance is not configured.");
     9         return;
    10     }
    11  
    12     long t1 = System.nanoTime();
    13  
    14     // Start the new server
    15     try {
    16         getServer().start();
    17     } catch (LifecycleException e) {
    18         log.fatal(sm.getString("catalina.serverStartFail"), e);
    19         try {
    20             getServer().destroy();
    21         } catch (LifecycleException e1) {
    22             log.debug("destroy() failed for failed Server ", e1);
    23         }
    24         return;
    25     }
    26  
    27     long t2 = System.nanoTime();
    28     if(log.isInfoEnabled()) {
    29         log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
    30     }
    31  
    32     // Register shutdown hook
    33     if (useShutdownHook) {
    34         if (shutdownHook == null) {
    35             shutdownHook = new CatalinaShutdownHook();
    36         }
    37         Runtime.getRuntime().addShutdownHook(shutdownHook);
    38  
    39         // If JULI is being used, disable JULI's shutdown hook since
    40         // shutdown hooks run in parallel and log messages may be lost
    41         // if JULI's hook completes before the CatalinaShutdownHook()
    42         LogManager logManager = LogManager.getLogManager();
    43         if (logManager instanceof ClassLoaderLogManager) {
    44             ((ClassLoaderLogManager) logManager).setUseShutdownHook(
    45                     false);
    46         }
    47     }
    48  
    49     if (await) {
    50         await();
    51         stop();
    52     }
    53 }

    load方法解析server.xml配置文件,并加载Server、Service、Connector、Container、Engine、Host、Context、Wrapper一系列的容器。加载完成后,调用getServer().start()来开启一个新的Server。

    下面先看load方法怎么加载组件和容器的:

     1 /**
     2   * Start a new server instance.
     3   */
     4  public void load() {
     5  
     6      long t1 = System.nanoTime();
     7  
     8      initDirs();
     9  
    10      // Before digester - it may be needed
    11      initNaming();
    12  
    13      // Create and execute our Digester
    14      Digester digester = createStartDigester();
    15  
    16      InputSource inputSource = null;
    17      InputStream inputStream = null;
    18      File file = null;
    19      file = configFile();
    20      inputStream = new FileInputStream(file);
    21      inputSource = new InputSource(file.toURI().toURL().toString());
    22      inputSource.setByteStream(inputStream);
    23      digester.push(this);
    24      digester.parse(inputSource);
    25      
    26  
    27      getServer().setCatalina(this);
    28      getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    29      getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
    30  
    31      // Stream redirection
    32      initStreams();
    33  
    34      // Start the new server
    35      getServer().init();
    36  }

    首先利用Digester类解析server.xml文件,得到容器的配置,并创建相应的对象,并关联父子容器。依次创建的是StandardServer、StandardService、StandardEngine、StandardHost。

    然后拿到StandardServer实例调用init()方法初始化Tomcat容器的一系列组件。一些容器初始化的的时候,都会调用其子容器的init()方法,初始化它的子容器。顺序是StandardServer、StandardService、StandardEngine、Connector。每个容器都在初始化自身相关设置的同时,将子容器初始化。

    这里插入一个Tomcat中生命周期的概念。在初始化、开启一系列组件、容器的过程中,由tomcat'管理的组件和容器,都有一个共同的特点,都实现了org.apache.catalina.Lifecycle接口,由Tomcat管理其生命周期。Lifecycle提供一种统一的管理对象生命周期的接口。通过Lifecycle、LifecycleListener、LifecycleEvent,Catalina实现了对tomcat各种组件、容器统一的启动和停止的方式。

    在Tomcat服务开启过程中启动的一些列组件、容器,都继承了org.apache.catalina.util.LifecycleBase这个抽象类,其中的init()、start() 方法、stop() 方法,为其子类实现了统一的start和stop管理。方法中具体的initInternal()、startInternal() 和stopInternal() 方法,交由子类自己实现。

    看一下LifecycleBase的init()和start()的实现吧:

    org.apache.catalina.util.LifecycleBase#start
     1 public final synchronized void init() throws LifecycleException {
     2     if (!state.equals(LifecycleState.NEW)) {
     3         invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
     4     }
     5  
     6     try {
     7         setStateInternal(LifecycleState.INITIALIZING, null, false);
     8         initInternal();
     9         setStateInternal(LifecycleState.INITIALIZED, null, false);
    10     } catch (Throwable t) {
    11         ExceptionUtils.handleThrowable(t);
    12         setStateInternal(LifecycleState.FAILED, null, false);
    13         throw new LifecycleException(
    14                 sm.getString("lifecycleBase.initFail",toString()), t);
    15     }
    16 }
    17  
    18  
    19 public final synchronized void start() throws LifecycleException {
    20  
    21     if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
    22             LifecycleState.STARTED.equals(state)) {
    23  
    24         if (log.isDebugEnabled()) {
    25             Exception e = new LifecycleException();
    26             log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
    27         } else if (log.isInfoEnabled()) {
    28             log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
    29         }
    30  
    31         return;
    32     }
    33  
    34     if (state.equals(LifecycleState.NEW)) {
    35         init();
    36     } else if (state.equals(LifecycleState.FAILED)) {
    37         stop();
    38     } else if (!state.equals(LifecycleState.INITIALIZED) &&
    39             !state.equals(LifecycleState.STOPPED)) {
    40         invalidTransition(Lifecycle.BEFORE_START_EVENT);
    41     }
    42  
    43     try {
    44         setStateInternal(LifecycleState.STARTING_PREP, null, false);
    45         startInternal();
    46         if (state.equals(LifecycleState.FAILED)) {
    47             stop();
    48         } else if (!state.equals(LifecycleState.STARTING)) {
    49             invalidTransition(Lifecycle.AFTER_START_EVENT);
    50         } else {
    51             setStateInternal(LifecycleState.STARTED, null, false);
    52         }
    53     } catch (Throwable t) {
    54         ExceptionUtils.handleThrowable(t);
    55         setStateInternal(LifecycleState.FAILED, null, false);
    56         throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
    57     }
    58 }

    可以看到,init()和start()方法里,调用了initInternal()方法、startInternal()方法和stop()方法,这三者最终会走子类的具体实现。

    上面的StandardServer的初始化过程就是一个活生生的例子。在Catalina的load过程中,getServer().init()方法就是LifecycleBase中的init()方法,调用initInternal()时是走的StandardServer的实现,StandardServer的initInternal()中会调用StandardServer的init()方法,进行子容器的初始化。然后依次初始化。

    看一下代码,了解一下StandardServer中的initInternal()实现。

     1 /**
     2  * Invoke a pre-startup initialization. This is used to allow connectors
     3  * to bind to restricted ports under Unix operating environments.
     4  */
     5 @Override
     6 protected void initInternal() throws LifecycleException {
     7  
     8     super.initInternal();
     9  
    10     // Register global String cache
    11     // Note although the cache is global, if there are multiple Servers
    12     // present in the JVM (may happen when embedding) then the same cache
    13     // will be registered under multiple names
    14     onameStringCache = register(new StringCache(), "type=StringCache");
    15  
    16     // Register the MBeanFactory
    17     MBeanFactory factory = new MBeanFactory();
    18     factory.setContainer(this);
    19     onameMBeanFactory = register(factory, "type=MBeanFactory");
    20  
    21     // Register the naming resources
    22     globalNamingResources.init();
    23  
    24     // Populate the extension validator with JARs from common and shared
    25     // class loaders
    26     if (getCatalina() != null) {
    27         ClassLoader cl = getCatalina().getParentClassLoader();
    28         // Walk the class loader hierarchy. Stop at the system class loader.
    29         // This will add the shared (if present) and common class loaders
    30         while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
    31             if (cl instanceof URLClassLoader) {
    32                 URL[] urls = ((URLClassLoader) cl).getURLs();
    33                 for (URL url : urls) {
    34                     if (url.getProtocol().equals("file")) {
    35                         try {
    36                             File f = new File (url.toURI());
    37                             if (f.isFile() &&
    38                                     f.getName().endsWith(".jar")) {
    39                                 ExtensionValidator.addSystemResource(f);
    40                             }
    41                         } catch (URISyntaxException e) {
    42                             // Ignore
    43                         } catch (IOException e) {
    44                             // Ignore
    45                         }
    46                     }
    47                 }
    48             }
    49             cl = cl.getParent();
    50         }
    51     }
    52     // Initialize our defined Services
    53     for (int i = 0; i < services.length; i++) {
    54         services[i].init();
    55     }
    56 }

    再举一个具体的例子:

    回到刚才的启动过程中,getServer().start()开启服务的方法,实际就是上面提到的LifecycleBase中的start()方法。其中,会调用org.apache.catalina.core.StandardServer#initInternal方法,初始化Server并调用Service的init方法。org.apache.catalina.core.StandardServer在其实现的startInternal() 中,开启naming resources和services,调用service的start方法,开启所有service,调用其service的startInternal()方法。

    下面看一下StandardServer中的startInternal()的实现:

    org.apache.catalina.core.StandardServer#startInternal
     1 protected void startInternal() throws LifecycleException {
     2  
     3     fireLifecycleEvent(CONFIGURE_START_EVENT, null);
     4     setState(LifecycleState.STARTING);
     5  
     6     globalNamingResources.start();
     7  
     8     // Start our defined Services
     9     synchronized (servicesLock) {
    10         for (int i = 0; i < services.length; i++) {
    11             services[i].start();
    12         }
    13     }
    14 }

    这里的service,是org.apache.catalina.core.StandardService的实例。

    总结一下启动的Tomcat启动的过程

    在Catalina的load方法里,就已经调用了StandardServer里的init方法,一层一层初始化了globalNamingResources,StandardService--》StandardEngine,executors,MapperListener,Connector--》CoyoteAdapter,protocolHandler。至此就将tomcat的catalina中的组件、容器初始化完成。 接下来就是调用start方法一层一层开启,StandardServer的startInternal方法,按层次start:globalNamingResources,StandardService--》StandardEngine,executors,MapperListener,Connector--》StandardHost,StandardContext,protocolHandler。顺序基本同init过程。StandardEngine在start时,会init子容器,并调用子容器的start方法。子容器依次这样init、start,就开启了StandardHost和StandardContext。

     

    参考文章:

    tomcat源码分析-Connector初始化与启动

    tomcat源码分析-Container初始化与加载

    tomcat源码分析-http请求在Container中的执行路线

    tomcat源码解析(一)--启动与Server.xml文件的解析

  • 相关阅读:
    Mina、Netty、Twisted一起学(七):公布/订阅(Publish/Subscribe)
    MySQL高可用之——keepalived+互为主从
    JS之BOM
    Mac下利用(xcode)安装git
    计算矩阵边缘元素之和
    什么是猴子补丁(monkey patch)
    协议支撑
    BZOJ 3727 PA2014 Final Zadanie 树形DP
    Linux cat命令
    iOS8新特性
  • 原文地址:https://www.cnblogs.com/z941030/p/8524882.html
Copyright © 2011-2022 走看看