Tomcat启动和停止

很明显,我们启动或停止Tomcat,一般调用的是bin下的startup.sh或shutdown.sh(以Linux为例,以下涉及到平台,若无特殊说明,一般都指Linux)。我们看看startup.sh的脚本是什么?

# -----------------------------------------------------------------------------
# Start Script for the CATALINA Server
# ----------------------------------------------------------------------------- # Better OS/ detection: see Bugzilla
os400=false
case "`uname`" in
OS400*) os400=true;;
esac # resolve links - $ may be a softlink
PRG="$0" while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`/"$link"
fi
done PRGDIR=`dirname "$PRG"`
EXECUTABLE=catalina.sh # Check that target executable exists
if $os400; then
# -x will Only work on the os400 if the files are:
# . owned by the user
# . owned by the PRIMARY group of the user
# this will not work if the user belongs in secondary groups
eval
else
if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
echo "Cannot find $PRGDIR/$EXECUTABLE"
echo "The file is absent or does not have execute permission"
echo "This file is needed to run this program"
exit
fi
fi exec "$PRGDIR"/"$EXECUTABLE" start "$@"
最后一行,它执行的是"/"$EXECUTABLE,那它是什么呢?参看前面的一行
EXECUTABLE=catalina.sh
是的,启动脚本还是调用的catalina.sh.
接下来我们看看catalina.sh脚本有什么内容。
代码比较长,为了看的更明白,删除一些不必要的,完整的脚本大家可以参看catalina.sh
#!/bin/sh

# Control Script for the CATALINA Server
#
# Environment Variable Prerequisites
#
# Do not set the variables in this script. Instead put them into a script
# setenv.sh in CATALINA_BASE/bin to keep your customizations separate.
#
# CATALINA_HOME May point at your Catalina "build" directory.
#
# CATALINA_BASE (Optional) Base directory for resolving dynamic portions
# of a Catalina installation. If not present, resolves to
# the same directory that CATALINA_HOME points to.
#
# CATALINA_OUT (Optional) Full path to a file where stdout and stderr
# will be redirected.
# Default is $CATALINA_BASE/logs/catalina.out
#
# # JAVA_HOME Must point at your Java Development Kit installation.
# Required to run the with the "debug" argument.
#
# JRE_HOME Must point at your Java Runtime installation.
# Defaults to JAVA_HOME if empty. If JRE_HOME and JAVA_HOME
# are both set, JRE_HOME is used.
#
# JAVA_OPTS (Optional) Java runtime options used when any command
# is executed.
# Include here and not in CATALINA_OPTS all options, that
# should be used by Tomcat and also by the stop process,
# the version command etc.
# Most options should go into CATALINA_OPTS.
# Djava.util.logging.config.file=$CATALINA_BASE/conf/logging.properties"
## ----------------------------------------------------------------------------- # ----- Execute The Requested Command ----------------------------------------- elif [ "$1" = "start" ] ; then if [ -z "$CATALINA_OUT_CMD" ] ; then
touch "$CATALINA_OUT"
catalina_out_command=">> \"$CATALINA_OUT\" 2>&1"
else
catalina_out_command="| $CATALINA_OUT_CMD"
fi
if [ ! -z "$CATALINA_PID" ]; then
catalina_pid_file="$CATALINA_PID"
else
catalina_pid_file=/dev/null
fi
if [ "$1" = "-security" ] ; then
if [ $have_tty -eq ]; then
echo "Using Security Manager"
fi
shift
eval \{ $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
-D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \
-classpath "\"$CLASSPATH\"" \
-Djava.security.manager \
-Djava.security.policy=="\"$CATALINA_BASE/conf/catalina.policy\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
org.apache.catalina.startup.Bootstrap "$@" start \
\>\& \& echo \$! \>\"$catalina_pid_file\" \; \} $catalina_out_command "&" else
eval \{ $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
-D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \
-classpath "\"$CLASSPATH\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
org.apache.catalina.startup.Bootstrap "$@" start \
\>\& \& echo \$! \>\"$catalina_pid_file\" \; \} $catalina_out_command "&" fi echo "Tomcat started." elif [ "$1" = "stop" ] ; then eval "\"$_RUNJAVA\"" $JAVA_OPTS \
-D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \
-classpath "\"$CLASSPATH\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
org.apache.catalina.startup.Bootstrap "$@" stop elif [ "$1" = "version" ] ; then "$_RUNJAVA" \
-classpath "$CATALINA_HOME/lib/catalina.jar" \
org.apache.catalina.util.ServerInfo else echo "Usage: catalina.sh ( commands ... )"
echo "commands:"
if $os400; then
echo " debug Start Catalina in a debugger (not available on OS400)"
echo " debug -security Debug Catalina with a security manager (not available on OS400)"
else
echo " debug Start Catalina in a debugger"
echo " debug -security Debug Catalina with a security manager"
fi
echo " jpda start Start Catalina under JPDA debugger"
echo " run Start Catalina in the current window"
echo " run -security Start in the current window with security manager"
echo " start Start Catalina in a separate window"
echo " start -security Start in a separate window with security manager"
echo " stop Stop Catalina, waiting up to 5 seconds for the process to end"
echo " stop n Stop Catalina, waiting up to n seconds for the process to end"
echo " stop -force Stop Catalina, wait up to 5 seconds and then use kill -KILL if still running"
echo " stop n -force Stop Catalina, wait up to n seconds and then use kill -KILL if still running"
echo " configtest Run a basic syntax check on server.xml - check exit code for result"
echo " version What version of tomcat are you running?"
echo "Note: Waiting for the process to end and use of the -force option require that \$CATALINA_PID is defined"
exit
从以上脚本可以看出,无论是start还是stop,其实调用的都是org.apache.catalina.startup.Bootstrap 这个类。
 
整理一下,Tomcat整个程序的入口在Boostrap这个类这里。
OK,我们看看Bootstrap的启动过程。

Bootstrap的启动过程

Bootstrap这个类在以下package里

org.apache.catalina.startup

Bootstrap类有个main方法,这是Tomcat的入口。

public static void main(String args[]) {

    synchronized (daemonLock) {
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;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to
// prevent a range of class not found exceptions.
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
} 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();
if (null == daemon.getServer()) {
System.exit(1);
}
} 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);
} }

代码比较简单,也比较直观。
首先创建一个Bootstrap的实例,然后调用init方法。接着处理main方法的传入参数,是start还是stop等,当然默认是start了。
再看看init的方法

public void init() throws Exception {

    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.getConstructor().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; }

我们可以看到初始化ClassLoader,接着会创建一个startupClass的类,就是这段代码:

Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
我们看到startup的类其实就是org.apache.catalina.startup.Catalina,最后创建了另外一个实例catalinaDaemon。Tomcat的启动,停止都是调用它。
 
我们看看start方法启动Tomcat
public void start()
throws Exception {
if( catalinaDaemon==null ) init(); Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
method.invoke(catalinaDaemon, (Object [])null); }
这里首先判断有没有初始化,如果没有,调用init并对其初始化,然后使用Method进行反射调用Catalina的start方法。
 
:Method是java.lang.reflect包里的类,代表了一个具体的方法,在这里,只是start方法。Method.invoke方法是指执行该方法,它有2个参数,第一个参数是方法名,第二个是该方法的参数。

Catalina的启动

追本溯源,最后Tomcat的管理落到了Catalina这个类了,所以Catalina才是最重要的类。
先看看Catalina的方法。

我们可以清晰看到几个重要的方法:
  • await
  • load
  • start
  • stop
  • usage
对于这几个方法,也算是直观。那么我们从简单的开始说起。
方法usage
见以下代码,其实就是显示catalina的用法。
/**
* Print usage information for this application.
*/
protected void usage() { System.out.println(sm.getString("catalina.usage")); }
方法await
 
代码如下
/**
* Await and shutdown.
*/
public void await() { getServer().await(); }
这个方法搭配setAwait来用,setAwait方法用于设置Server启动完成后是否进入等待状态,如果为true,则进入,否则不进入。
 
方法load
/**
* Start a new server instance.
*/
public void load() { if (loaded) {
return;
}
loaded = true; long t1 = System.nanoTime(); initDirs(); // Before digester - it may be needed
initNaming(); // Set configuration source
ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
File file = configFile(); // Create and execute our Digester
Digester digester = createStartDigester(); try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
InputStream inputStream = resource.getInputStream();
InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
} catch (Exception e) {
if (file == null) {
log.warn(sm.getString("catalina.configFail", getConfigFile() + "] or [server-embed.xml"), e);
} else {
log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
if (file.exists() && !file.canRead()) {
log.warn(sm.getString("catalina.incorrectPermissions"));
}
}
return;
} getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // 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(sm.getString("catalina.initError"), e);
}
} long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info(sm.getString("catalina.init", Long.valueOf((t2 - t1) / 1000000)));
}
}
这个方法会加载conf/server.xml这个配置文件,并且将我们前面描述到的Server,Listener,Service,Engine等逐一加载。而且Server实例已经由Digester创建,以下是createStartDigester的实现,部分代码删除。
protected Digester createStartDigester() {
long t1=System.currentTimeMillis();
// Initialize the digester
Digester digester = new Digester();
digester.setValidating(false);
digester.setRulesValidation(true);
Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
// Ignore className on all elements
List<String> objectAttrs = new ArrayList<>();
objectAttrs.add("className");
fakeAttributes.put(Object.class, objectAttrs);
// Ignore attribute added by Eclipse for its internal tracking
List<String> contextAttrs = new ArrayList<>();
contextAttrs.add("source");
fakeAttributes.put(StandardContext.class, contextAttrs);
// Ignore Connector attribute used internally but set on Server
List<String> connectorAttrs = new ArrayList<>();
connectorAttrs.add("portOffset");
fakeAttributes.put(Connector.class, connectorAttrs);
digester.setFakeAttributes(fakeAttributes);
digester.setUseContextClassLoader(true); // 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"); digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl"); digester.addObjectCreate("Server/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener"); digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service"); digester.addObjectCreate("Server/Service/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Listener");
digester.addSetNext("Server/Service/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener"); //Executor
digester.addObjectCreate("Server/Service/Executor",
"org.apache.catalina.core.StandardThreadExecutor",
"className");
digester.addSetProperties("Server/Service/Executor"); digester.addSetNext("Server/Service/Executor",
"addExecutor",
"org.apache.catalina.Executor"); digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(
new String[]{"executor", "sslImplementationName", "protocol"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector"); digester.addRule("Server/Service/Connector", new AddPortOffsetRule()); digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
"org.apache.tomcat.util.net.SSLHostConfig");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
digester.addSetNext("Server/Service/Connector/SSLHostConfig",
"addSslHostConfig",
"org.apache.tomcat.util.net.SSLHostConfig"); digester.addObjectCreate("Server/Service/Connector/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/Listener");
digester.addSetNext("Server/Service/Connector/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener"); digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
"addUpgradeProtocol",
"org.apache.coyote.UpgradeProtocol"); // Add RuleSets for nested elements
digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/")); // When the 'engine' is found, set the parentClassLoader.
digester.addRule("Server/Service/Engine",
new SetParentClassLoaderRule(parentClassLoader));
addClusterRuleSet(digester, "Server/Service/Engine/Cluster/"); long t2=System.currentTimeMillis();
if (log.isDebugEnabled()) {
log.debug("Digester for server.xml created " + ( t2-t1 ));
}
return digester; }
方法start
start用来启动一个Server,具体代码如下
/**
* Start a new server instance.
*/
public void start() { if (getServer() == null) {
load();
} if (getServer() == null) {
log.fatal(sm.getString("catalina.noServer"));
return;
} long t1 = System.nanoTime(); // Start the new server
try {
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
} long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info(sm.getString("catalina.startup", Long.valueOf((t2 - t1) / 1000000)));
} // 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是否存在,如果不存在,调用load初始化一下。接着调用Server的start方法启动它。最后如果启用关闭的钩子,还要注册一下该钩子。在末尾,如果如果await设置为true的话,需要调用await和stop。Await会直接调用Server的await方法,Server内部会进入一个while循环,以下代码是Server的await方法的实现。当await里while结束,执行stop方法,停止服务器。

public void await() {
// Set up a server socket to wait on
try {
awaitSocket = new ServerSocket(getPortWithOffset(), 1,
InetAddress.getByName(address));
} catch (IOException e) {
log.error(sm.getString("standardServer.awaitSocket.fail", address,
String.valueOf(getPortWithOffset()), String.valueOf(getPort()),
String.valueOf(getPortOffset())), e);
return;
} try {
awaitThread = Thread.currentThread(); // Loop waiting for a connection and a valid command
while (!stopAwait) {
ServerSocket serverSocket = awaitSocket;
if (serverSocket == null) {
break;
} // Wait for the next connection
Socket socket = null;
StringBuilder command = new StringBuilder();
try {
InputStream stream;
long acceptStartTime = System.currentTimeMillis();
try {
socket = serverSocket.accept();
socket.setSoTimeout(10 * 1000); // Ten seconds
stream = socket.getInputStream();
} catch (SocketTimeoutException ste) {
// This should never happen but bug 56684 suggests that
// it does.
log.warn(sm.getString("standardServer.accept.timeout",
Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste);
continue;
} catch (AccessControlException ace) {
log.warn(sm.getString("standardServer.accept.security"), ace);
continue;
} catch (IOException e) {
if (stopAwait) {
// Wait was aborted with socket.close()
break;
}
log.error(sm.getString("standardServer.accept.error"), e);
break;
} // Read a set of characters from the socket
int expected = 1024; // Cut off to avoid DoS attack
while (expected < shutdown.length()) {
if (random == null)
random = new Random();
expected += (random.nextInt() % 1024);
}
while (expected > 0) {
int ch = -1;
try {
ch = stream.read();
} catch (IOException e) {
log.warn(sm.getString("standardServer.accept.readError"), e);
ch = -1;
}
// Control character or EOF (-1) terminates loop
if (ch < 32 || ch == 127) {
break;
}
command.append((char) ch);
expected--;
}
} finally {
// Close the socket now that we are done with it
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
// Ignore
}
} // Match against our command string
boolean match = command.toString().equals(shutdown);
if (match) {
log.info(sm.getString("standardServer.shutdownViaPort"));
break;
} else
log.warn(sm.getString("standardServer.invalidShutdownCommand", command.toString()));
}
} finally {
ServerSocket serverSocket = awaitSocket;
awaitThread = null;
awaitSocket = null; // Close the server socket and return
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
// Ignore
}
}
}
}

Server的启动

前面也提到了Catalina的start其实调用的是Server的start方法,我们看看Server的start的方法怎么实现的。在深入前,我们看看Server的方法列表等信息。
Server是一个接口,继承了LifeCycle这个接口。
Server提供了2个方法addService()以及removeService(),分别用来增加或删除Service。在前面我们讲过,一个Server是可以包含多个Service的。
在Server 启动前,Server包含的几个Service也需要初始化,其实每个Service的初始化也是用Service本身的init方法来初始化。
Server的默认实现是org.apache.catalina.core.StandardServer,StandardServer又继承了LifeCycleMBeanBase,
public final class StandardServer extends LifecycleMBeanBase implements Server 
LifeCycleMBeanBase又继承了LifecycleBase
public abstract class LifecycleMBeanBase extends LifecycleBase 
LifeCycleBase又实现了LifeCycle
public abstract class LifecycleBase implements Lifecycle
因为init和start都是LifeCycleBase里,这也是tomcat的生命周期重要的方法。Init和start也分别调用了initInternal和startInternal。至于怎么实现,还得看子类的具体逻辑,前面说到StandardServer是默认的实现,那我们看看StandardServer的具体实现逻辑。继续上代码:
protected void initInternal() throws LifecycleException {

    super.initInternal();

     //… …

    // Initialize our defined Services

    for (int i = 0; i < services.length; i++) {

        services[i].init();

    }

}
上面是initInternal的代码,我们发现其实就是调用Server包含的Service的init方法。
下面是startInternal的代码。 
protected void startInternal() throws LifecycleException {

fireLifecycleEvent(CONFIGURE_START_EVENT, null);

setState(LifecycleState.STARTING);

globalNamingResources.start();

// Start our defined Services

synchronized (servicesLock) {

for (int i = 0; i < services.length; i++) {

services[i].start();

}

}

if (periodicEventDelay > 0) {

monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(

new Runnable() {

@Override

public void run() {

startPeriodicLifecycleEvent();

}

}, 0, 60, TimeUnit.SECONDS);

}

}

首先需要启动各个Service,这也是通过Service 的start方法来实现。

Service的启动过程

前面已经说到,Server会调用Service的start方法来启动各个Service,我们继续看Service的启动。Service的默认实现是org.apache.catalina.core.StandardService,StandardService也继承了LifeCycleMBeanBase类,所以init和start最终也会调用initInternal和startInternal这两个方法。还是先看StandardService的方法列表。

和StandardServer类似,一个Service可能包含多个Connector,所以我们看到了addConnector和removeConnector这两个方法。
我们可以看到initInternal和startInternal两个方法,我们抽取核心的代码在这里展示一下:

@Override
protected void initInternal() throws LifecycleException { super.initInternal(); if (engine != null) {
engine.init();
} // Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
} // Initialize mapper listener
mapperListener.init(); // Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
connector.init();
}
}
} protected void startInternal() throws LifecycleException { if(log.isInfoEnabled())
log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING); // Start our defined Container first
if (engine != null) {
synchronized (engine) {
engine.start();
}
} synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
} mapperListener.start(); // Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
}

根据以上代码可以看出,无论是initInternal还是startInternal,都涉及到了Engine,Executor,Listener以及Connector。
这里需要说明的是Executor,前面并未提及,因为Tomcat默认把它注释掉了,其实它就是Connector中管理线程的线程池。

<!--The connectors can use a shared executor, you can define one or more named thread pools-->

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>

如何使用Executor?只需要在Connector加上executor这个属性,并指向定义的Executor。

<Connector executor = “tomcatThreadPool” port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />

综合上述,现在我们可以了解Tomcat的启动过程了,先初始化,然后在启动。
Startup.sh -> Bootstrap的main -> Catalina -> StandardServer -> StandardService->Container->Executor->Connector

深入浅出Tomcat/2 - Tomcat启动和停止的更多相关文章

  1. Tomcat源码分析——启动与停止服务

    前言 熟悉Tomcat的工程师们,肯定都知道Tomcat是如何启动与停止的.对于startup.sh.startup.bat.shutdown.sh.shutdown.bat等脚本或者批处理命令,大家 ...

  2. Tomcat7.0源代码分析——启动与停止服务原理

    前言 熟悉Tomcat的project师们.肯定都知道Tomcat是怎样启动与停止的. 对于startup.sh.startup.bat.shutdown.sh.shutdown.bat等脚本或者批处 ...

  3. CentOS7 增加tomcat 启动,停止,使用systemctl进行配置

    1,centos7 使用 systemctl 替换了 service命令 参考:redhat文档: https://access.redhat.com/documentation/en-US/Red_ ...

  4. centos7中使用yum安装tomcat以及它的启动、停止、重启

    centos7中使用yum安装tomcat 介绍 Apache Tomcat是用于提供Java应用程序的Web服务器和servlet容器. Tomcat是Apache Software Foundat ...

  5. CentOS下启动和停止Tomcat

    启动Tomcat: 进入tomcat目录/bin,然后./startup.sh 停止Tomcat: 进入tomcat目录/bin,然后./shutdown.sh

  6. tomcat启动、停止和重启脚本

    脚本名称:r.sh 脚本用途:启动.停止和重启tomcat 脚本参数:$1:[start|stop|restart] #!/bin/bash BIN_PATH="/tomcat_path/b ...

  7. Linux下Tomcat启动、停止、重新启动

    在Linux系统下,重启Tomcat使用命令操作的! 1.首先,进入Tomcat下的bin目录,${CATALINA_HOME}代表tomcat的安装路径 进入Tomcat安装目录: cd ${CAT ...

  8. tomcat 启动,停止,查看端口,日志位置

    1.启动之前先看看是否已经启动tomcat ,避免端口被占用 ps -ef|grep tomcat 2.启动:进入tomcat下的“bin”目录 输入命令:./startup.sh 3.查看tomca ...

  9. tomcat启动和停止脚本

    #!/bin/bash JDK_HOME=/apps/jdk1.7.0_79 CATALINA_HOME=/apps/tomcat export JDK_HOME CATALINA_HOME sour ...

随机推荐

  1. 章节二、2-String 引用数据类型-字符串类

    一.创建String(字符串对象)的两种方式 1.String str1 = "nihao"("nihao"值存储在常量值中) 2.String str2 = ...

  2. (网页)HTMl5的sessionStorage和localStorage

    百度上百度的,记录一下: html5中的Web Storage包括了两种存储方式:sessionStorage和localStorage. sessionStorage用于本地存储一个会话(sessi ...

  3. gitlab runner安装与使用

    今天来讲一下如何使用gitlab-runner 下载runner,根据自己对应服务器的型号自行选择下载: # Linux x86- sudo wget -O /usr/local/bin/gitlab ...

  4. A problem has been detected and windows has been shut down to prevent damage to your computer.他么啥意思?看这里!【蓝屏】

    A problem has been detected and windows has been shut down to prevent damage to your computer.  检测到问 ...

  5. mssql sqlserver 验证整型函数分享

    转自:http://www.maomao365.com/?p=6227 摘要: 下文将制作一个isnumber验证整型的函数,供在sql脚本中做数值判断,如下所示: 例: 实现原理:判断 是否包含特殊 ...

  6. java中利用dom4j解析XML文件

    官网下载Dom4j地址:https://dom4j.github.io/ 注意:使用Dom4j开发,需下载dom4j相应的jar文件 题目:后台利用dom4j解析student.xml文件,并返回Li ...

  7. Shell按行读取文件的3种方法

    Shell按行读取文件的方法有很多,常见的三种方法如下: 要读取的文件: [root@mini05 -]# cat file.info 写法一: [root@mini05 -]# cat read1. ...

  8. March 04th, 2018 Week 10th Sunday

    Tomorrow never comes. 我生待明日,万事成蹉跎. Most of my past failures can be chalked up to the bad habit of pr ...

  9. VMware安装系统时"无法创建新虚拟机: 不具备执行此操作的权限"的解决方案

    作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 在VMware中安装操作系统时,遇到以下这种情况 问题主要出在虚拟机文件的位置选择上,不应该选在VMwa ...

  10. Glyphicons 字体图标