tomcat 中的 bootstrap 与 catalina

10 篇文章 0 订阅
tomcat中各个组件的生命周期是由server控制的。那么server的生命周期由谁控制呢? 

我们先来看下使用脚本启动tomcat的时候,首先会发生什么。

java应用要运行,需要一个main方法。tomcat启动的时候调用的是bootstrap中的main方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
  * Main method, used for testing only.
  *
  * @param args Command line arguments to be processed
  */
public static void main(String args[]) {
 
     if (daemon == null ) {
         // Don't set daemon until init() has completed
         Bootstrap bootstrap = new Bootstrap();
         try {
             bootstrap.init();
         } catch (Throwable t) {
             handleThrowable(t);
             t.printStackTrace();
             return ;
         }
         daemon = bootstrap;
     }
 
     try {
         String command = "start" ;
         if (args.length > 0 ) {
             command = args[args.length - 1 ];
         }
 
         if (command.equals( "startd" )) {
             args[args.length - 1 ] = "start" ;
             daemon.load(args);
             daemon.start();
         } else if (command.equals( "stopd" )) {
             args[args.length - 1 ] = "stop" ;
             daemon.stop();
         } else if (command.equals( "start" )) {
             daemon.setAwait( true );
             daemon.load(args);
             daemon.start();
         } else if (command.equals( "stop" )) {
             daemon.stopServer(args);
         } else if (command.equals( "configtest" )) {
             daemon.load(args);
             if ( null ==daemon.getServer()) {
                 System.exit( 1 );
             }
             System.exit( 0 );
         } else {
             log.warn( "Bootstrap: command \"" + command + "\" does not exist." );
         }
     } catch (Throwable t) {
         // Unwrap the Exception for clearer error reporting
         if (t instanceof InvocationTargetException &&
                 t.getCause() != null ) {
             t = t.getCause();
         }
         handleThrowable(t);
         t.printStackTrace();
         System.exit( 1 );
     }
 
}
这个main方法首先会new一个Bootstrap对象,并且把这个对象放到一个static域中。 
然后会对这个对象进行初始化,初始化方法如下: 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
  * Initialize daemon.
  */
public void init()
     throws Exception
{
 
     // Set Catalina path
     setCatalinaHome();
     setCatalinaBase();
 
     initClassLoaders();
 
     Thread.currentThread().setContextClassLoader(catalinaLoader);
 
     SecurityClassLoad.securityClassLoad(catalinaLoader);
 
     // Load our startup class and call its process() method
     if (log.isDebugEnabled())
         log.debug( "Loading startup class" );
     Class<?> startupClass =
         catalinaLoader.loadClass
         ( "org.apache.catalina.startup.Catalina" );
     Object startupInstance = startupClass.newInstance();
 
     // Set the shared extensions class loader
     if (log.isDebugEnabled())
         log.debug( "Setting startup class properties" );
     String methodName = "setParentClassLoader" ;
     Class<?> paramTypes[] = new Class[ 1 ];
     paramTypes[ 0 ] = Class.forName( "java.lang.ClassLoader" );
     Object paramValues[] = new Object[ 1 ];
     paramValues[ 0 ] = sharedLoader;
     Method method =
         startupInstance.getClass().getMethod(methodName, paramTypes);
     method.invoke(startupInstance, paramValues);
 
     catalinaDaemon = startupInstance;
 
}

在这个init方法中,会new一个catalina对象。bootstrap的大多数操作,诸如start、stop、load等,实际上都是调用的这个catalina对象的相应方法。


在bootstrap的初始化完成之后,会根据用户输入的main函数的参数(start、stop等),判断执行什么操作。
如果是start参数,就表明是启动tomcat。会先后执行daemon.setAwait、daemon.load、daemon.start这三个方法。他们最终会分别采用反射的方式去调用之前已经初始化的catalina的对应方法setAwait、load、start。也就是说其实bootstrap就调用了catalina的方法,本身是没做什么额外动作的。
stop则表明是关闭tomcat。

?
1
2
3
4
/**
  * Daemon object used by main.
  */
private static Bootstrap daemon = null ;

这里需要注意一点,我们使用脚本启动和关闭tomcat的时候,实际上最终都是执行bootstrap的main方法,正因为daemon是static的,所以,我们start和stop的时候,实际上操作的是同一个bootstrap对象,才能对同一个tomcat的启动和关闭。 

如上面所说,在启动过程中,daemon.load会调用catalina的load方法。catalina的load方法如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/**
  * Start a new server instance.
  */
public void load() {
 
     long t1 = System.nanoTime();
 
     initDirs();
 
     // Before digester - it may be needed
 
     initNaming();
 
     // Create and execute our Digester
     Digester digester = createStartDigester();
 
     InputSource inputSource = null ;
     InputStream inputStream = null ;
     File file = null ;
     try {
         file = configFile();
         inputStream = new FileInputStream(file);
         inputSource = new InputSource( "file://" + file.getAbsolutePath());
     } catch (Exception e) {
         if (log.isDebugEnabled()) {
             log.debug(sm.getString( "catalina.configFail" , file), e);
         }
     }
     if (inputStream == null ) {
         try {
             inputStream = getClass().getClassLoader()
                 .getResourceAsStream(getConfigFile());
             inputSource = new InputSource
                 (getClass().getClassLoader()
                  .getResource(getConfigFile()).toString());
         } catch (Exception e) {
             if (log.isDebugEnabled()) {
                 log.debug(sm.getString( "catalina.configFail" ,
                         getConfigFile()), e);
             }
         }
     }
 
     // This should be included in catalina.jar
     // Alternative: don't bother with xml, just create it manually.
     if ( inputStream== null ) {
         try {
             inputStream = getClass().getClassLoader()
                     .getResourceAsStream( "server-embed.xml" );
             inputSource = new InputSource
             (getClass().getClassLoader()
                     .getResource( "server-embed.xml" ).toString());
         } catch (Exception e) {
             if (log.isDebugEnabled()) {
                 log.debug(sm.getString( "catalina.configFail" ,
                         "server-embed.xml" ), e);
             }
         }
     }
 
 
     if (inputStream == null || inputSource == null ) {
         if  (file == null ) {
             log.warn(sm.getString( "catalina.configFail" ,
                     getConfigFile() + "] or [server-embed.xml]" ));
         } else {
             log.warn(sm.getString( "catalina.configFail" ,
                     file.getAbsolutePath()));
             if (file.exists() && !file.canRead()) {
                 log.warn( "Permissions incorrect, read permission is not allowed on the file." );
             }
         }
         return ;
     }
 
     try {
         inputSource.setByteStream(inputStream);
         digester.push( this );
         digester.parse(inputSource);
     } catch (SAXParseException spe) {
         log.warn( "Catalina.start using " + getConfigFile() + ": " +
                 spe.getMessage());
         return ;
     } catch (Exception e) {
         log.warn( "Catalina.start using " + getConfigFile() + ": " , e);
         return ;
     } finally {
         try {
             inputStream.close();
         } catch (IOException e) {
             // Ignore
         }
     }
 
     getServer().setCatalina( this );
 
     // Stream redirection
     initStreams();
 
     // Start the new server
     try {
         getServer().init();
     } catch (LifecycleException e) {
         if (Boolean.getBoolean( "org.apache.catalina.startup.EXIT_ON_INIT_FAILURE" )) {
             throw new java.lang.Error(e);
         } else {
             log.error( "Catalina.start" , e);
         }
 
     }
 
     long t2 = System.nanoTime();
     if (log.isInfoEnabled()) {
         log.info( "Initialization processed in " + ((t2 - t1) / 1000000 ) + " ms" );
     }
 
}

这个方法实际上是在加载、解析server.xml,生成tomcat的组件:server、service、connector、engine、host、context等。最后调用server的init方法。 
我们之前提到过tomcat中server用来管理整个tomcat的生命周期

daemon.load方法执行之后,会执行daemon.start,这个方法调用catalina的start方法,catalina调用server的start方法,从而启动tomcat的各个组件。

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


server start之后,catalina会创建一个 CatalinaShutdownHook 对象,然后将它添加到运行时的shutdownHook中。即注册一个虚拟机的shutdown hook,这样在我们不通过脚本关闭tomcat,而是直接杀死进程的时候,也能够执行catalina的stop方法,完成tomcat的关闭过程。 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
  * Shutdown hook which will perform a clean shutdown of Catalina if needed.
  */
protected class CatalinaShutdownHook extends Thread {
 
     @Override
     public void run() {
         try {
             if (getServer() != null ) {
                 Catalina. this .stop();
             }
         } catch (Throwable ex) {
             ExceptionUtils.handleThrowable(ex);
             log.error(sm.getString( "catalina.shutdownHookFail" ), ex);
         } finally {
             // If JULI is used, shut JULI down *after* the server shuts down
             // so log messages aren't lost
             LogManager logManager = LogManager.getLogManager();
             if (logManager instanceof ClassLoaderLogManager) {
                 ((ClassLoaderLogManager) logManager).shutdown();
             }
         }
     }
}

The Java virtual machine shuts down in response to two kinds of events:
1、程序正常退出,包括最后一个non-daemon线程退出或者Runtime.exit、System.exit方法被调用。 
2、java虚拟机被终止,包括被用户中断,如:^C,或者系统级的事件:用户登出、系统关闭等。 
发生上面的情况的时候,虚拟机就会调用所有的shutdown hook。 

综上,tomcat启动过程实际就是bootstrap调用catalina的start方法,然后catalina调用server的start方法。关闭的时候,可以是bootstrap的stop方法调用catalina的stop方法,也可以是Hook调用catalina的stop方法,然后catalina的stop方法调用server的stop方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值