Tomcat源码解析(十):启动和关闭

之前一直说,catalina是tomcat的一个重要组成。Server、connector、container都归Catalina来管理,它的主要功能就是,在启动的时候读取server.xml文件,创建server对象,也就创建了server管理的connector、container。在应用程序关闭的时候,它进行一些善后的工作。

shutdown

应用程序关闭的时候,需要做一些清理现场的工作。如果程序正常关闭,可以调用catalina.stop()进行清理。可是如果用户键入ctrl+c或者未关闭程序的情况下退出系统,会导致jvm强制关闭,这时候怎么办呢?

于是就有了ShutdownHook。

ShutdownHook是交给系统的,当程序异常关闭的时候,虚拟机会调用启动之前已经注册的ShutdownHook的run(),来进行清理工作。

// --------------------------------------- CatalinaShutdownHook Inner Class


/**
 * CatalinaShutdownHook是catalina的内部类,调用server的stop方法清理内部的connector、container等
 * Shutdown hook which will perform a clean shutdown of Catalina if needed.
 */
protected class CatalinaShutdownHook extends Thread {

    public void run() {

        if (server != null) {
            try {
                ((Lifecycle) server).stop();
            } catch (LifecycleException e) {
                System.out.println("Catalina.stop: " + e);
                e.printStackTrace(System.out);
                if (e.getThrowable() != null) {
                    System.out.println("----- Root Cause -----");
                    e.getThrowable().printStackTrace(System.out);
                }
            }
        }

    }
}

catalina.start

catalina在启动时,读取server.xml文件,创建Server,这是用digester实现的。

/**
* Startup/Shutdown shell program for Catalina.  The following command line
* options are recognized:
* <ul>
* <li><b>-config {pathname}</b> - Set the pathname of the configuration file
*     to be processed.  If a relative path is specified, it will be
*     interpreted as relative to the directory pathname specified by the
*     "catalina.base" system property.   [conf/server.xml]
* <li><b>-help</b> - Display usage information.
* <li><b>-stop</b> - Stop the currently running instance of Catalina.
* </u>
*
* @author Craig R. McClanahan
* @version $Revision: 1.48 $ $Date: 2002/05/23 17:22:37 $
*/

public class Catalina {


/**
 * Pathname to the server configuration file.
 */
protected String configFile = "conf/server.xml";


/**
 * 管理的Server
 * The server component we are starting or stopping
 */
protected Server server = null;

 /**
 * The application main program.
 *
 * @param args Command line arguments
 */
public static void main(String args[]) {

    (new Catalina()).process(args);
}


/**
 * The instance main program.
 *
 * @param args Command line arguments
 */
public void process(String args[]) {

    setCatalinaHome();
    setCatalinaBase();
    try {
        if (arguments(args))
            execute();
    } catch (Exception e) {
        e.printStackTrace(System.out);
    }
}

/**
 * Execute the processing that has been configured from the command line.
 */
protected void execute() throws Exception {

    if (starting)
        start();
    else if (stopping)
        stop();

}

 /**
 * 创建digester,用于读取server.xml
 * Create and configure the Digester we will be using for startup.
 */
protected Digester createStartDigester() {

    // Initialize the digester
    Digester digester = new Digester();
    if (debug)
        digester.setDebug(999);
    digester.setValidating(false);

    // Configure the actions we will be using
    digester.addObjectCreate("Server",
                             "org.apache.catalina.core.StandardServer",
                             "className");
    digester.addSetProperties("Server");
    digester.addSetNext("Server",
                        "setServer",
                        "org.apache.catalina.Server");//在start()中首先push了catalina,所以此处是调用catalina.setServer()

    digester.addObjectCreate("Server/GlobalNamingResources",
                             "org.apache.catalina.deploy.NamingResources");
    digester.addSetProperties("Server/GlobalNamingResources");
    digester.addSetNext("Server/GlobalNamingResources",
                        "setGlobalNamingResources",
                        "org.apache.catalina.deploy.NamingResources");

    //此处...省略一大段

    return (digester);

}

  /**
 * Start a new server instance.
 */
protected void start() {

    // Create and execute our Digester
    Digester digester = createStartDigester();
    File file = configFile();
    try {
        InputSource is =
            new InputSource("file://" + file.getAbsolutePath());
        FileInputStream fis = new FileInputStream(file);
        is.setByteStream(fis);
        digester.push(this);
        digester.parse(is);
        fis.close();
    } catch (Exception e) {
        System.out.println("Catalina.start: " + e);
        e.printStackTrace(System.out);
        System.exit(1);
    }

    // Setting additional variables
    if (!useNaming) {
        System.setProperty("catalina.useNaming", "false");
    } else {
        System.setProperty("catalina.useNaming", "true");
        String value = "org.apache.naming";
        String oldValue =
            System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
        if (oldValue != null) {
            value = value + ":" + oldValue;
        }
        System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
        value = System.getProperty
            (javax.naming.Context.INITIAL_CONTEXT_FACTORY);
        if (value == null) {
            System.setProperty
                (javax.naming.Context.INITIAL_CONTEXT_FACTORY,
                 "org.apache.naming.java.javaURLContextFactory");
        }
    }

    // If a SecurityManager is being used, set properties for
    // checkPackageAccess() and checkPackageDefinition
    if( System.getSecurityManager() != null ) {
        String access = Security.getProperty("package.access");
        if( access != null && access.length() > 0 )
            access += ",";
        else
            access = "sun.,";
        Security.setProperty("package.access",
            access + "org.apache.catalina.,org.apache.jasper.");
        String definition = Security.getProperty("package.definition");
        if( definition != null && definition.length() > 0 )
            definition += ",";
        else
            definition = "sun.,";
        Security.setProperty("package.definition",
            // FIX ME package "javax." was removed to prevent HotSpot
            // fatal internal errors
            definition + "java.,org.apache.catalina.,org.apache.jasper.");
    }

    // Replace System.out and System.err with a custom PrintStream
    SystemLogHandler log = new SystemLogHandler(System.out);
    System.setOut(log);
    System.setErr(log);

    Thread shutdownHook = new CatalinaShutdownHook();

    // Start the new server
    if (server instanceof Lifecycle) {
        try {
            server.initialize();
            ((Lifecycle) server).start();
            try {
                //注册shutdownhook到系统中。如果程序异常关闭,就无法从server.await()返回,就会执行shutdownhook.run(),来进行清理
                // Register shutdown hook
                Runtime.getRuntime().addShutdownHook(shutdownHook);
            } catch (Throwable t) {
                // This will fail on JDK 1.2. Ignoring, as Tomcat can run
                // fine without the shutdown hook.
            }
            //await()建立一个单独的线程,等待shutdown指令,收到shutdown指令后返回,就会执行下面的server.stop(),进行关闭操作
            // Wait for the server to be told to shut down
            server.await();
        } catch (LifecycleException e) {
            System.out.println("Catalina.start: " + e);
            e.printStackTrace(System.out);
            if (e.getThrowable() != null) {
                System.out.println("----- Root Cause -----");
                e.getThrowable().printStackTrace(System.out);
            }
        }
    }

    // Shut down the server
    if (server instanceof Lifecycle) {
        try {
            try {
                // Remove the ShutdownHook first so that server.stop()
                // doesn't get invoked twice
                Runtime.getRuntime().removeShutdownHook(shutdownHook);
            } catch (Throwable t) {
                // This will fail on JDK 1.2. Ignoring, as Tomcat can run
                // fine without the shutdown hook.
            }
            ((Lifecycle) server).stop();
        } catch (LifecycleException e) {
            System.out.println("Catalina.stop: " + e);
            e.printStackTrace(System.out);
            if (e.getThrowable() != null) {
                System.out.println("----- Root Cause -----");
                e.getThrowable().printStackTrace(System.out);
            }
        }
    }

}

catalina.stop

catalina.process收到关闭命令时,调用stop()进行清理工作。

“`

/**
   * Stop an existing server instance.
   */
  protected void stop() {

  // Create and execute our Digester
  //这里只读取了server部分,是为了回收前一个Server吗??
  Digester digester = createStopDigester();
  File file = configFile();
  try {
      InputSource is =
          new InputSource("file://" + file.getAbsolutePath());
      FileInputStream fis = new FileInputStream(file);
      is.setByteStream(fis);
      digester.push(this);
      digester.parse(is);
      fis.close();
  } catch (Exception e) {
      System.out.println("Catalina.stop: " + e);
      e.printStackTrace(System.out);
      System.exit(1);
  }

// Stop the existing server
try {
    //发送shutdown命令,await线程收到后,从await()返回,即可执行server.stop()
    Socket socket = new Socket("127.0.0.1", server.getPort());
    OutputStream stream = socket.getOutputStream();
    String shutdown = server.getShutdown();
    for (int i = 0; i < shutdown.length(); i++)
        stream.write(shutdown.charAt(i));
    stream.flush();
    stream.close();
    socket.close();
} catch (IOException e) {
    System.out.println("Catalina.stop: " + e);
    e.printStackTrace(System.out);
    System.exit(1);
}
}

 /**
     * Create and configure the Digester we will be using for shutdown.
 */
protected Digester createStopDigester() {

// Initialize the digester
Digester digester = new Digester();
if (debug)
    digester.setDebug(999);

// Configure the rules we need for shutting down
//只读取了server部分
digester.addObjectCreate("Server",
                         "org.apache.catalina.core.StandardServer",
                         "className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
                    "setServer",
                    "org.apache.catalina.Server");

return (digester);

}

stop()中创建了一个新的digester,读取server.xml文件。不过读取的内容要少一些,只创建了一个简单的Server对象,没有创建内部的connector、container。然后从这个server中,拿到shutdown命令,并发送。这里为什么要创建一个新的server呢?

我的理解是,创建一个新的server,catalina就不再包含旧的server的引用。这样,等待shutdown命令的线程收到命令并结束,旧的server从await()返回后,旧的server就可以被回收了。

启动脚本

tomcat实际启动,不是手动运行Bootstrap.jar来执行的,而是sh catalina.sh start这样启动脚本来启动。tomcat下bin目录下有多个.sh文件,包括startup.sh catalina.sh shutdown.sh等。startup.sh shutdown.sh实际都是调用了catalina.sh,只是传递了不同的参数。这个可以通过Catalina类的代码也可以看出,传递不同的参数,会执行不同的操作,包括开始和结束。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值