Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

深入浅出Tomcat/2 - Tomcat启动和停止

张太国 2019-01-31 09:50:00 阅读数:247 评论数:0 点赞数:0 收藏数:0

Tomcat启动和停止

很明显,我们启动或停止Tomcat,一般调用的是bin下的startup.sh或shutdown.sh(以Linux为例,以下涉及到平台,若无特殊说明,一般都指Linux)。我们看看startup.sh的脚本是什么?/# -----------------------------------------------------------------------------/# Start Scriptforthe CATALINA Server /#-----------------------------------------------------------------------------/# Better OS/400 detection: see Bugzilla 31132os400=false case "uname" inOS400/) os400=true;;esac/# resolve links- $0may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : './-> (./)$'`if expr "$link" : '/./' > /dev/null; thenPRG="$link" elsePRG=dirname "$PRG"/"$link" fi donePRGDIR=dirname "$PRG" EXECUTABLE=catalina.sh/# Check that target executable existsif $os400; then/#-x will Only work on the os400 ifthe files are: /#1. owned by the user /#2. owned by the PRIMARY group of the user /# this will not workif the user belongs in secondary groupsevalelse 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"exit1 fi fiexec"$PRGDIR"/"$EXECUTABLE" start "$@"

最后一行,它执行的是"/"$EXECUTABLE,那它是什么呢?参看前面的一行EXECUTABLE=catalina.sh

是的,启动脚本还是调用的catalina.sh.接下来我们看看catalina.sh脚本有什么内容。

代码比较长,为了看的更明白,删除一些不必要的,完整的脚本大家可以参看catalina.sh/#!/bin/sh/# Control Scriptforthe CATALINA Server /# /# Environment Variable Prerequisites /# /# Do not set the variablesinthis script. Instead put them into a script /# setenv.sh in CATALINABASE/bin to keep your customizations separate. /# /# CATALINAHOME May point at your Catalina"build"directory. /# /# CATALINABASE (Optional) Base directoryforresolving dynamic portions /# of a Catalina installation. If not present, resolves to /# the same directory that CATALINAHOME points to. /# /# CATALINAOUT (Optional) Full path to afilewhere stdout and stderr /# will be redirected. /# Default is $CATALINABASE/logs/catalina.out /# /# /# JAVAHOME Must point at your Java Development Kit installation. /# Required to run the with the"debug"argument. /# /# JREHOME Must point at your Java Runtime installation. /# Defaults to JAVAHOMEifempty. If JREHOME and JAVAHOME /# are both set, JREHOME is used. /# /# JAVAOPTS (Optional) Java runtime options used when any command /# is executed. /# Include here and notinCATALINAOPTS all options, that /# should be used by Tomcat and also by the stop process, /# the version command etc. /# Most options should go into CATALINAOPTS. /# Djava.util.logging.config.file=$CATALINABASE/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" elsecatalina_out_command="| $CATALINA_OUT_CMD" fi if [ ! -z "$CATALINA_PID" ]; thencatalina_pid_file="$CATALINA_PID" elsecatalina_pid_file=/dev/null fi if [ "$1" = "-security" ] ; then if [ $have_tty -eq 1 ]; then echo "Using Security Manager" fi shifteval { $_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 2>&1 & echo $! >"$catalina_pid_file" ; } $catalina_out_command"&" elseeval { $_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 2>&1 & echo $! >"$catalina_pid_file" ; } $catalina_out_command"&" fi echo "Tomcat started." elif [ "$1" = "stop" ] ; theneval""$_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"$@"stopelif [ "$1" = "version" ] ; then "$_RUNJAVA"-classpath "$CATALINA_HOME/lib/catalina.jar" org.apache.catalina.util.ServerInfoelse 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"exit1

从以上脚本可以看出,无论是start还是stop,其实调用的都是 *org.apache.catalina.startup.Bootstrap*这个类。 

整理一下,Tomcat整个程序的入口在Boostrap这个类这里。OK,我们看看Bootstrap的启动过程。

Bootstrap的启动过程

Bootstrap这个类在以下package里org.apache.catalina.startup

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

public static voidmain(String args[]) {synchronized(daemonLock) {if (daemon == null) {//Don't set daemon until init() has completed Bootstrap bootstrap = newBootstrap();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() throwsException { 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方法启动Tomcatpublic voidstart()throwsException {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 voidusage() { System.out.println(sm.getString("catalina.usage")); }

 方法await

 代码如下

//// Await and shutdown.// public voidawait() { getServer().await(); }

 这个方法搭配 setAwait来用, setAwait方法用于设置Server启动完成后是否进入等待状态,如果为true,则进入,否则不进入。

 方法load

//// Start a new server instance.// public voidload() {if(loaded) {return; } loaded= true;long t1 =System.nanoTime(); initDirs();//Before digester - it may be needed initNaming();//Set configuration source ConfigFileLoader.setSource(newCatalinaBaseConfigurationSource(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= newInputSource(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 newjava.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的实现,部分代码删除。

protectedDigester createStartDigester() {long t1=System.currentTimeMillis();//Initialize the digester Digester digester = newDigester(); digester.setValidating(false); digester.setRulesValidation(true); Map, List> fakeAttributes = new HashMap<>();//Ignore className on all elements List objectAttrs = new ArrayList<>(); objectAttrs.add("className"); fakeAttributes.put(Object.class, objectAttrs);//Ignore attribute added by Eclipse for its internal tracking List contextAttrs = new ArrayList<>(); contextAttrs.add("source"); fakeAttributes.put(StandardContext.class, contextAttrs);//Ignore Connector attribute used internally but set on Server List 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",newConnectorCreateRule()); digester.addRule("Server/Service/Connector", newSetAllPropertiesRule(new String[]{"executor", "sslImplementationName", "protocol"})); digester.addSetNext("Server/Service/Connector","addConnector","org.apache.catalina.connector.Connector"); digester.addRule("Server/Service/Connector", newAddPortOffsetRule()); 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",newSetParentClassLoaderRule(parentClassLoader)); addClusterRuleSet(digester,"Server/Service/Engine/Cluster/");long t2=System.currentTimeMillis();if(log.isDebugEnabled()) { log.debug("Digester for server.xml created " + ( t2-t1 )); }returndigester; }

 方法start

start用来启动一个Server,具体代码如下//// Start a new server instance.// public voidstart() {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= newCatalinaShutdownHook(); } 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 instanceofClassLoaderLogManager) { ((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 voidawait() {//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= newStringBuilder();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 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; }elselog.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又继承了LifecycleBasepublic abstract class LifecycleMBeanBase extends LifecycleBase 

LifeCycleBase又实现了LifeCyclepublic abstract class LifecycleBase implements Lifecycle

因为init和start都是LifeCycleBase里,这也是tomcat的生命周期重要的方法。Init和start也分别调用了initInternal和startInternal。至于怎么实现,还得看子类的具体逻辑,前面说到StandardServer是默认的实现,那我们看看StandardServer的具体实现逻辑。继续上代码:protected void initInternal() throwsLifecycleException {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() throwsLifecycleException { fireLifecycleEvent(CONFIGURESTARTEVENT,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(newRunnable() { @Overridepublic voidrun() { 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两个方法,我们抽取核心的代码在这里展示一下:@Overrideprotected void initInternal() throwsLifecycleException {super.initInternal();if (engine != null) { engine.init(); }//Initialize any Executors for(Executor executor : findExecutors()) {if (executor instanceofJmxEnabled) { ((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() throwsLifecycleException {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中管理线程的线程池。

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

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

版权声明
本文为[张太国]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/confach/p/10306716.html

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;

支付宝红包,每日可领