基本组件
Tomcat顶层容器是Server,即一个应用服务器,对应于PC机的一个主进程。Server中包含一个或多个Service,用于提供具体的应用服务。Service主要包含Container和Connector两部分,Container内包装了具体的Web应用和服务,Connector主要处理请求连接相关的内容。
主要的组件类图:
Container.png
Tomcat启动过程
Boostrap类是tomcat启动的入口,启动tomcat实际上是调用了这个类的main方法:
public final class Bootstrap {
...
public static void main(String args[]) {
if (daemon == null) {
// 新建一个Bootstrap
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 > 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();
} 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) {
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
...
}
整个过程并不复杂,首先新建一个Bootstrap实例并通过init方法来完成Catalina类的初始化,然后对命令行参数进行处理,默认执行start命令。start命令的处理主要通过三个方法来完成:setAwait(true), load(args), start()。这三个方法分别通过反射的方式来调用已经初始化的Catalina实例,实际上整个启动过程是由Catalina完成的。
- Catalina的启动过程
Catalina的启动过程主要通过三个方法来完成:
public class Catalina {
public void setAwait(boolean b) {
await = b;
}
public void load() {
long t1 = System.nanoTime();
// 创建server过程省略,主要是通过Digester读取server.xml配置文件,完成一些属性赋值
getServer().setCatalina(this);
try {
getServer().init();//调用Server的init加载所有Service
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error("Catalina.start", e);
}
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
}
}
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();
//启动Server实例
try {
getServer().start();//调用Server的start方法,启动所有加载的Service
} 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) / 1000000) + " ms");
}
// 省略注册关闭钩子代码
if (await) {
await();
stop();
}
}
}
load方法完成对配置文件的解析和server实例属性设置,然后通过start方法调用Server的start方法。Server被定义为一个接口,包含具体的服务管理方法。最后,通过Server的await方法来启动一个监听循环,当这个循环退出时stop方法才会被调用,从而停止服务器。
- Server的启动过程
Server接口定义了一系列的Service管理和服务器生命周期管理方法,Server默认实现类是StandardServer, 这个类继承自LifecycleBase,在init的时候调用了initInternal方法,start的时候调用startInternal方法,并且这两个模板方法被StandardServer重写:
public final class StandardServer extends LifecycleMBeanBase implements Server {
@Override
protected void initInternal() throws LifecycleException {
...
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
@Override
protected void startInternal() throws LifecycleException {
...
// Start our defined Services
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}
}
通过以上两个调用就完成了所有Service的初始化和启动,随后通过await方法来使服务器进入等待状态:
@Override
public void await() {
if( port == -2 ) {
//直接退出.
return;
}
if( port==-1 ) {
try {
awaitThread = Thread.currentThread();
while(!stopAwait) {
try {
Thread.sleep( 10000 );
} catch( InterruptedException ex ) {
// 无法通过8005端口命令退出
}
}
} finally {
awaitThread = null;
}
return;
}
// Set up a server socket to wait on
try {
awaitSocket = new ServerSocket(port, 1,
InetAddress.getByName(address));
} catch (IOException e) {
log.error("StandardServer.await: create[" + address
+ ":" + port
+ "]: ", 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) {
log.warn(sm.getString("standardServer.accept.timeout",
Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste);
continue;
} catch (AccessControlException ace) {
log.warn("StandardServer.accept security exception: "
+ ace.getMessage(), ace);
continue;
} catch (IOException e) {
if (stopAwait) {
// Wait was aborted with socket.close()
break;
}
log.error("StandardServer.await: accept: ", e);
break;
}
...
// Match against our command string
boolean match = command.toString().equals(shutdown);
if (match) {
log.info(sm.getString("standardServer.shutdownViaPort"));
break;
} else
log.warn("StandardServer.await: Invalid command '"
+ command.toString() + "' received");
}
} finally {
ServerSocket serverSocket = awaitSocket;
awaitThread = null;
awaitSocket = null;
// Close the server socket and return
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
// Ignore
}
}
}
}
通过一个while循环来建立ServerSocket监听客户端连接,同时会在该端口监听客户端的shutdown命令,主要配置是在conf/server.xml文件中:
<Server port="8005" shutdown="SHUTDOWN">
...
</Server>
通过设置这个端口为-1即可禁用网络命令停止tomcat服务。
- Service的启动过程
Service是开发者部署在tomcat上的web应用抽象概念,这个接口的默认实现是StandardService,也继承自LifestyleBase类,相关的初始化和加载主要通过调用initInternal和startInternal来完成:
public class StandardService extends LifecycleMBeanBase implements Service {
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
if (container != null) {
container.init();
}
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof LifecycleMBeanBase) {
((LifecycleMBeanBase) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
connector.init();
}
}
}
@Override
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 (container != null) {
synchronized (container) {
container.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
// Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
try {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
}
}
}
主要调用了container,executors和connectors的init和start来完成相关应用组件的加载,executors是在connector中管理的线程池,可以通过server.xml进行配置,默认是注释的:
<Service name="Catalina">
<!--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"/>
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
</Service>
通过以上配置就完成了connector的线城池配置。整个启动过程至此就完成了,主要包括init和start两个阶段,分别完成各个组件的初始化和启动过程,如下图:
startup.png
网友评论