tomcat启动过程
tomcat中最顶层的容器叫server,代表服务器,server中起码有一个service,提供服务。service中包含两部分:connector和Container。connector处理连接相关的功能,包括socket与request、response的转化。Container负责封装管理servlet以及处理request请求。一个tomcat只有一个server,一个server包含多个service,一个service只有一个Container,但是可以有多个connector(使用相同协议或者不同协议提供不同端口的连接),tomcat结构如图所示:

tomcat的Server由org.apache.catalina.startup.Catalina来管理,Catalina是整个tomcat的管理类,有load、start、stop三个方法来管理整个服务器的生命周期,load方法用于根据conf/server.xml文件创建server并根据init方法进行初始化;start方法启动服务器,stop方法停止服务器;start和stop方法分别调用server的start和stop方法,load调用server的init方法,然后这三个方法在按容器结构逐层调用对应方法。例如server的start方法会调用service方法,service的start方法又会调用connectors和container的start方法,然后整个tomcat服务就启动了。init方法和start方法也类似,这就是tomcat的生命周期。Catalina还有个await方法,Catalina的await方法调用了server的await方法,使的主线程不会退出。
tomcat的main方法不在Catalina中,而是在org.apache.catalina.startup.Bootstrap中,Bootstrap作用类似于CatalinaAdaptor,具体处理过程使用Catalina完成,这样启动入口和管理类可以分开,可以方便的创造出多种启动方式,每种方式只要设计对应的CatalinaAdaptor即可。
Bootstrap的启动过程
bootstrap是tomcat的入口,启动tomcat即是调用bootstrap的main方法:
public static void main(String[] args)
{
if (daemon == null)
{
Bootstrap bootstrap = new Bootstrap();
try
{
bootstrap.init();
}
catch (Throwable t)
{
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
}
else
{
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
try
{
String command = "start";
if (args.length > ) {
command = args[(args.length - )];
}
if (command.equals("startd"))
{
args[(args.length - )] = "start";
daemon.load(args);
daemon.start();
}
else if (command.equals("stopd"))
{
args[(args.length - )] = "stop";
daemon.stop();
}
else if (command.equals("start"))
{
daemon.setAwait(true);
daemon.load(args);
daemon.start();
}
else if (command.equals("stop"))
{
daemon.stopServer(args);
}
else if (command.equals("configtest"))
{
daemon.load(args);
if (null == daemon.getServer()) {
System.exit();
}
System.exit();
}
else
{
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
}
catch (Throwable t)
{
if (((t instanceof InvocationTargetException)) && (t.getCause() != null)) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit();
}
}
main方法有两部分内容:首先创建Bootstrap,并执行init方法;然后处理main的参数,如果参数为空,默认执行start方法。
在init中初始化了ClassLoader,并使用Classloader创建了Catalina实例,赋值给catalinaDaemon,后面操作都使用catalinaDaemon来执行。
对于start命令处理调用了三个方法:setAwait(true)、load(args)和start(),这三个方法内部都调用了Catalina的相应方法,不过是通过反射来调用的。start方法(另外两个也类似):
public void start() throws Exception {
if (this.catalinaDaemon == null) {
init();
}
Method method = this.catalinaDaemon.getClass().getMethod("start", (Class[])null);
method.invoke(this.catalinaDaemon, (Object[])null);
}
首先判断catalinaDaemon有没有初始化,没有的话进行init,然后通过Method进行反射调用Catalina的start方法。
Catalina的启动过程
Catalina主要调用setAwait、load和start来完成启动。setAwait用于设置Server启动完成后时候进入等待状态;load用于加载配置文件,并初始化server;start用于启动服务器。
setAwait方法如下:
public void setAwait(boolean b)
{
this.await = b;
}
setAwait方法就是设置await的值,start方法启动服务器后会使用它的值来判断是否进入等待状态。
Catalina的load方法根据conf/server.xml来创建server方法,并进行赋值,然后调用server的init方法:
public void load()
{
long t1 = System.nanoTime();
initDirs();
initNaming();
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try
{
try
{
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
}
catch (Exception e)
{
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail", new Object[] { file }), e);
}
}
if (inputStream == null) {
try
{
inputStream = getClass().getClassLoader().getResourceAsStream(getConfigFile());
inputSource = new InputSource(getClass().getClassLoader().getResource(getConfigFile()).toString());
}
catch (Exception e)
{
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail", new Object[] { getConfigFile() }), e);
}
}
}
if (inputStream == null) {
try
{
inputStream = getClass().getClassLoader().getResourceAsStream("server-embed.xml");
inputSource = new InputSource(getClass().getClassLoader().getResource("server-embed.xml").toString());
}
catch (Exception e)
{
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail", new Object[] { "server-embed.xml" }), e);
}
}
}
if ((inputStream == null) || (inputSource == null))
{
if (file == null)
{
log.warn(sm.getString("catalina.configFail", new Object[] { getConfigFile() + "] or [server-embed.xml]" }));
}
else
{
log.warn(sm.getString("catalina.configFail", new Object[] { file.getAbsolutePath() }));
if ((file.exists()) && (!file.canRead())) {
log.warn("Permissions incorrect, read permission is not allowed on the file.");
}
}
return;
}
try
{
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
}
catch (SAXParseException spe)
{
log.warn("Catalina.start using " + getConfigFile() + ": " + spe.getMessage()); return;
}
catch (Exception e)
{
log.warn("Catalina.start using " + getConfigFile() + ": ", e); return;
}
if (inputStream != null) {
try
{
inputStream.close();
}
catch (IOException localIOException3) {}
}
getServer().setCatalina(this);
}
finally
{
if (inputStream != null) {
try
{
inputStream.close();
}
catch (IOException localIOException4) {}
}
}
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
initStreams();
try
{
getServer().init();
}
catch (LifecycleException e)
{
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new Error(e);
}
log.error("Catalina.start", e);
}
long t2 = System.nanoTime();
if (log.isInfoEnabled()) {
log.info("Initialization processed in " + (t2 - t1) / L + " ms");
}
}
Catalina的start方法主要调用server的start方法,并根据await来判断是否让程序进入等待状态:
public void start()
{
if (getServer() == null) {
load();
}
if (getServer() == null)
{
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
long t1 = System.nanoTime();
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("Server startup in " + (t2 - t1) / L + " ms");
}
if (this.useShutdownHook)
{
if (this.shutdownHook == null) {
this.shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
LogManager logManager = LogManager.getLogManager();
if ((logManager instanceof ClassLoaderLogManager)) {
((ClassLoaderLogManager)logManager).setUseShutdownHook(false);
}
}
if (this.await)
{
await();
stop();
}
}
先判断Server是否已经存在,如果不存在调用load来初始化Server,然后调用Server的start方法来启动服务器。并且注册关闭钩子并根据await判断程序是否进入等待状态。进入等待状态会调用await和stop两个方法,await调用server的await,server内部的await方法会执行while循环,此时程序就停在了await方法,当await退出时,就会执行stop方法关闭服务器。
Server启动过程
Server接口中提供addService(Service service)和removeService(Service service)来添加和删除Service,Server的start和init方法分别循环调用所有Service的init和start方法来启动所有的Service。
Server的默认实现是org.apache.catalina.core.StandardServer,StandardServer继承于LifecycleMBeanBase,lifecycleMbeanBase继承LifecycleBase,init和start方法定义在LifecycleBase中,LifecycleBase里的init和start调用initInternal和startInternal方法,这两个都是模板方法,由具体子类实现,所以StandardServer的init和start方法执行时会调用自己的initInternal和startInternal方法。StandardServer的initInternal和startInternal方法循环调用了每个service的start和init方法:
protected void startInternal() throws LifecycleException {
fireLifecycleEvent("configure_start", null);
setState(LifecycleState.STARTING);
this.globalNamingResources.start();
synchronized (this.servicesLock)
{
for (int i = ; i < this.services.length; i++) {
this.services[i].start();
}
}
}
protected void initInternal() throws LifecycleException {
super.initInternal();
this.onameStringCache = register(new StringCache(), "type=StringCache");
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
this.onameMBeanFactory = register(factory, "type=MBeanFactory");
this.globalNamingResources.init();
if (getCatalina() != null)
{
ClassLoader cl = getCatalina().getParentClassLoader();
while ((cl != null) && (cl != ClassLoader.getSystemClassLoader()))
{
if ((cl instanceof URLClassLoader))
{
URL[] urls = ((URLClassLoader)cl).getURLs();
for (URL url : urls) {
if (url.getProtocol().equals("file")) {
try
{
File f = new File(url.toURI());
if ((f.isFile()) && (f.getName().endsWith(".jar"))) {
ExtensionValidator.addSystemResource(f);
}
}
catch (URISyntaxException localURISyntaxException) {}catch (IOException localIOException) {}
}
}
}
cl = cl.getParent();
}
}
for (int i = ; i < this.services.length; i++) {
this.services[i].init();
}
}
StandardServer还实现了await方法,Catalina就是调用它使服务器进入等待状态:
public void await()
{
if (this.port == -) {
return;
}
if (this.port == -)
{
try
{
this.awaitThread = Thread.currentThread();
while (!this.stopAwait) {
try
{
Thread.sleep(L);
}
catch (InterruptedException localInterruptedException) {}
}
}
finally
{
this.awaitThread = null;
}
return;
}
try
{
this.awaitSocket = new ServerSocket(this.port, , InetAddress.getByName(this.address));
}
catch (IOException e)
{
log.error("StandardServer.await: create[" + this.address + ":" + this.port + "]: ", e);
return;
}
try
{
this.awaitThread = Thread.currentThread();
while (!this.stopAwait)
{
ServerSocket serverSocket = this.awaitSocket;
if (serverSocket == null) {
break;
}
Socket socket = null;
StringBuilder command = new StringBuilder();
try
{
long acceptStartTime = System.currentTimeMillis();
try
{
socket = serverSocket.accept();
socket.setSoTimeout();
stream = socket.getInputStream();
}
catch (SocketTimeoutException ste)
{
InputStream stream;
log.warn(sm.getString("standardServer.accept.timeout", new Object[] { Long.valueOf(System.currentTimeMillis() - acceptStartTime) }), ste);
try
{
if (socket != null) {
socket.close();
}
}
catch (IOException localIOException1) {}
continue;
}
catch (AccessControlException ace)
{
log.warn("StandardServer.accept security exception: " + ace.getMessage(), ace);
try
{
if (socket != null) {
socket.close();
}
}
catch (IOException localIOException2) {}
continue;
}
catch (IOException e)
{
if (this.stopAwait) {
try
{
if (socket != null) {
socket.close();
}
}
catch (IOException localIOException3) {}
}
log.error("StandardServer.await: accept: ", e);
}
InputStream stream;
int expected = ;
while (expected < this.shutdown.length())
{
if (this.random == null) {
this.random = new Random();
}
expected += this.random.nextInt() % ;
}
while (expected > )
{
int ch = -;
try
{
ch = stream.read();
}
catch (IOException e)
{
log.warn("StandardServer.await: read: ", e);
ch = -;
}
if ((ch < ) || (ch == )) {
break;
}
command.append((char)ch);
expected--;
}
try
{
if (socket != null) {
socket.close();
}
}
catch (IOException localIOException5) {}
match = command.toString().equals(this.shutdown);
}
finally
{
try
{
if (socket != null) {
socket.close();
}
}
catch (IOException localIOException6) {}
}
boolean match;
if (match)
{
log.info(sm.getString("standardServer.shutdownViaPort"));
break;
}
log.warn("StandardServer.await: Invalid command '" + command.toString() + "' received");
}
}
finally
{
ServerSocket serverSocket;
ServerSocket serverSocket = this.awaitSocket;
this.awaitThread = null;
this.awaitSocket = null;
if (serverSocket != null) {
try
{
serverSocket.close();
}
catch (IOException localIOException8) {}
}
}
}
await的大致逻辑为先判断端口号port:
- port为-2,直接退出
- port为-1,会进入while(!stopAwait)的循环,并且内部没有break语句,stopAwait只有调用了stop才会变成true,所以此情况下只有外部调用stop才会退出循环
- port为其他值,会进入while(!stopAwait)循环,同时在port所在端口启动serverSocket监听关闭命令,如果接受到了则使用break调出循环。
这里的port为在配置文件中设置的shutdown端口,如果不通过网络命令来关闭tomcat,将port设置为-1即可。await对于接受的参数进行了一定处理,ASCII码小于32及其后面的部分直接丢弃。
Service的启动
Service的默认实现为org.apache.catalina.core.StandardService,StandardService也继承LifecycleMbeanBase,所以init和start也会调用initInternal和startInternal:
protected void initInternal()
throws LifecycleException
{
super.initInternal();
if (this.container != null) {
this.container.init();
}
for (Executor executor : findExecutors())
{
if ((executor instanceof JmxEnabled)) {
((JmxEnabled)executor).setDomain(getDomain());
}
executor.init();
}
this.mapperListener.init();
synchronized (this.connectorsLock)
{
for (Connector connector : this.connectors) {
try
{
connector.init();
}
catch (Exception e)
{
String message = sm.getString("standardService.connector.initFailed", new Object[] { connector });
log.error(message, e);
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new LifecycleException(message);
}
}
}
}
}
protected void startInternal()
throws LifecycleException
{
if (log.isInfoEnabled()) {
log.info(sm.getString("standardService.start.name", new Object[] { this.name }));
}
setState(LifecycleState.STARTING);
if (this.container != null) {
synchronized (this.container)
{
this.container.start();
}
}
synchronized (this.executors)
{
for (Executor executor : this.executors) {
executor.start();
}
}
this.mapperListener.start();
synchronized (this.connectorsLock)
{
for (Connector connector : this.connectors) {
try
{
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
}
catch (Exception e)
{
log.error(sm.getString("standardService.connector.startFailed", new Object[] { connector }), e);
}
}
}
}
StandardService中的initInternal和startInternal方法主要调用container、executors、mapperListener、connectors的init和start方法。MapperListener是Mapper的监听器,用来监控container的变化,executor是用来在connector管理线程的线程池,在server.xml中有参考的用法,只不过是被注释的:
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
这样整个tomcat就完成了启动,tomcat的整个启动流程如下图: