1. 程式人生 > >Tomcat啟動過程源碼解讀

Tomcat啟動過程源碼解讀

mef err war hook ack super .info org 請求

根據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文件的解析

Tomcat啟動過程源碼解讀