频道栏目
首页 > 程序开发 > 软件开发 > 其他 > 正文
Tomcat源码解析(十):启动和关闭
2016-09-17 09:31:00         来源:进击吧!!  
收藏   我要投稿

之前一直说,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:
* 
*
-config {pathname} - 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] *
-help - Display usage information. *
-stop - Stop the currently running instance of Catalina. * * * @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类的代码也可以看出,传递不同的参数,会执行不同的操作,包括开始和结束。

点击复制链接 与好友分享!回本站首页
相关TAG标签 Tomcat源码
上一篇:RabbitMQ 测试方案及结果
下一篇:直播技术(从服务端到客户端)二
相关文章
图文推荐
文章
推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站