文档章节

tomcat 中的 bootstrap 与 catalina

元圈
 元圈
发布于 2012/07/22 11:25
字数 1401
阅读 3347
收藏 4
tomcat中各个组件的生命周期是由server控制的。那么server的生命周期由谁控制呢?

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

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

/**
     * 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域中。
然后会对这个对象进行初始化,初始化方法如下:
/**
     * 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。

/**
     * 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方法如下:

/**
     * 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的各个组件。

/**
     * 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的关闭过程。
/**
     * 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方法。

© 著作权归作者所有

元圈
粉丝 9
博文 10
码字总数 7662
作品 0
杭州
程序员
私信 提问
cronolog分割tomcat catalina.out 日志

日志分割的起因 随着时间的推移,catalina.out这个文件的尺寸将会越来越大,当需要检查日志内容时会导致文件难以打开,而且同时tomcat依旧在不断的向文件中输入内容,这也会降低tomcat的性能...

langtu329
2016/04/26
362
0
tomcat错误信息解决方案

org.apache.catalina.core.StandardServer await 严重: StandardServer.await: create[8005]: java.net.BindException: Address already in use at java.net.PlainSocketImpl.socketBind(Nat......

ok绷forever
2016/02/19
14
0
利用 cronolog 对 tomcat 7 进行日志切割

利用 cronolog 对 tomcat 进行日志切割 一、安装 软件 cronolog-1.6.2.tar.gz tar zxvf cronolog-1.6.2.tar.gz cd cronolog-1.6.2 ./configure && make && make install 二、配置 tomcat 找到......

jicki
2015/12/29
0
0
Tomcat 源码分析(-)启动过程分析

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

AaronSheng
2016/11/28
151
0
Linux下tomcat的catalina.out文件过大,以及目录更改解决办法

方法1—分割流 使用cronolog工具切分Tomcat的catalina.out日志文件 cronolog一个对日志切分的小工具,其主页在http://cronolog.org/,我们也可以用它来切分Apache的日志。 推荐用此方法,方法...

Carl_
2014/08/28
8.1K
0

没有更多内容

加载失败,请刷新页面

加载更多

JAVA 编写redisUtils工具类,防止高并发获取缓存出现并发问题

import lombok.extern.slf4j.Slf4j;import org.springframework.data.redis.core.BoundHashOperations;import org.springframework.data.redis.core.BoundValueOperations;import org.......

huangkejie
25分钟前
3
0
JMM内存模型(一)&volatile关键字的可见性

在说这个之前,我想先说一下计算机的内存模型: CPU在执行的时候,肯定要有数据,而数据在内存中放着呢,这里的内存就是计算机的物理内存,刚开始还好,但是随着技术的发展,CPU处理的速度越...

走向人生巅峰的大路
43分钟前
88
0
你对AJAX认知有多少(2)?

接着昨日内容,我们几天继续探讨ajax的相关知识点 提到ajax下面几个问题又是必须要了解的啦~~~ 8、在浏览器端如何得到服务器端响应的XML数据。 通过XMLHttpRequest对象的responseXMl属性 9、 ...

理性思考
52分钟前
4
0
正则表达式基础(一)

1.转义 转义的作用: 当某个字符在表达式中具有特殊含义,例如字符串引号中出现了引号,为了可以使用这些字符本身,而不是使用其在表达式中的特殊含义,则需要通过转义符“\”来构建该字符转...

清自以敬
55分钟前
4
0
idea中@Data标签getset不起作用

背景:换电脑以后在idea中有@data注解都不生效 解决办法:idea装个插件 https://blog.csdn.net/seapeak007/article/details/72911529...

栾小糖
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部