您的位置:首页 > 运维架构 > Tomcat

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

2016-09-14 14:49 537 查看
之前一直说,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类的代码也可以看出,传递不同的参数,会执行不同的操作,包括开始和结束。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  tomcat 源码