1 StandardServer职责
StandardServer是tomcat容器的最高层的组件,职责如下:
实现Tomcat一键启动关闭,管理全局 JDNI资源,管理子组件,阻塞tomcat主线程。
StandardServer子组件
2 运行流程
2.1 Bootstarp运行流程
Bootstarp作为tomcat启动类,JVM会调用main函数完成tomcat启动。在其内部流程如下:
-
1 实例化Bootstrap对象。
-
2 对Bootstrap对象init,其内部包括构建Tomcat类加载器,并实例化Catalina对象
-
3 以main函数args参数作为指令,通过调用Catalina类中不同的方法,完成相应的功能。
- startd:首先调用load方法,接着调用start方法,
- start:首先调用setAwait方法,接着调用load方法,最后调用start方法
- stopd:调stopServer方法
- configtest:调load方法
核心方法功能
- bootstrap.init():负责初始化Bootstrap,在初始化的过程最重要的就是构建Tomcat类加载器,并创建Catalina。
- bootstrap.load(args):负责通过反射调用Catalina.load方法,实现tomcat加载。
- bootstrap.start():负责通过反射调用Catalina.start方法,实现tomcat启动。
- bootstrap.stop():负责通过反射调用Catalina.start方法,实现tomcat停止。
- bootstrap.setAwait(true):负责通过反射调用Catalina.setAwait方法,设置tomcat是否需要启动一个Socket等待接受shutdown命令,用来停止Tomcat.
2.2 Catalina运行流程
image-
catalina.init():负责使用Digester解析Server.xml,并实例化Server.xml中描述的所有组件,之后调用调用Server组件初始化方法init().
-
catalina.start():负责调用Server组件start启动方法,之后给当前JVM进程注册一个钩子线程,该线程负责用来当tomcat被关闭时完成一些清理工作。最后调用Server组件await()方法阻塞当前线程(tomcat主线程),如果当前线程从await()方法返回表示tomcat被停止。
-
catalina.stop():负责向JVM清理掉注册的钩子线程,顺序调用Server组件停止方法stop()以及清理方法destroy()。
-
catalina.stopServer():负责顺序调用Server组件停止方法stop()以及清理方法destroy(),并向Tomcat指定的端口发送shutdown指令,停止tomcat主线程。
3 解析server.xml
Tomcat使用Digester解析server.xml,Digester是一款用于将xml转换为Java对象的事件驱动型工具,是对SAX的高层次的封装。相对于SAX可以为xml中每一个标签设置对应的解析规则。详见 Tomcat相关技术-Digester(二)
这里通过解析server.xml实例化StandardServer,并设置server.xml文件中定义的属性初始化。
server.xml配置
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
</Service>
....
</Server>
3.1 解析<Server>标签
<Server>标签用来表示当前StandardServer组件
//解析<Server>标签
/** 解析<server>标签实例化StandardServer对象,并push到操作栈中 **/
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
/** 解析<server>标签将标签中属性值映射到StandardServer对象中**/
digester.addSetProperties("Server");
/** 解析</server>标签将操作栈栈顶对象设置到次栈顶对象属性中**/
//将StandardServer对象设置到Catalina启动类对象的server属性中
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
StandardServer构造函数
public StandardServer() {
super();
globalNamingResources = new NamingResourcesImpl();
globalNamingResources.setContainer(this);
/** 判断是否开启JNDI服务 **/
if (isUseNaming()) {
namingContextListener = new NamingContextListener();
addLifecycleListener(namingContextListener);
} else {
namingContextListener = null;
}
}
将<server>标签属性映射到StandardServer对象属性中
/**
* Tomcat shutdown操作,对应字符串指令
*/
private String shutdown = "SHUTDOWN";
/**
* Tomcat ShutDown操作,服务端监听Socket端口号。
*/
private int port = 8005;
/**
* Tomcat ShutDown执行,服务端监听Socket地址。
*/
private String address = "localhost";
image
3.2 解析<GlobalNamingResources>标签
<GlobalNamingResources>标签中定义了全局JNDI资源,
//解析<Server>GlobalNamingResources>标签
/** 解析<GlobalNamingResources>标签实例化NamingResourcesImpl对象,并push到操作栈中 **/
digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
/** 解析<GlobalNamingResources>标签将标签中属性值映射到NamingResourcesImpl对象中**/
digester.addSetProperties("Server/GlobalNamingResources");
/** 解析</GlobalNamingResources>标签将操作栈栈顶对象作为次栈顶对象StandardServer.setGlobalNamingResources方法调用的参数,设置到StandardServer属性中**/
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
3.2.1 设置globalNamingResources属性
/**
* 设置globalNamingResources属性
*/
@Override
public void setGlobalNamingResources
(NamingResourcesImpl globalNamingResources) {
/** 获取设置前globalNamingResources **/
NamingResourcesImpl oldGlobalNamingResources =
this.globalNamingResources;
/** 设置globalNamingResources **/
this.globalNamingResources = globalNamingResources;
this.globalNamingResources.setContainer(this);
/** 触发属性变更通知 **/
support.firePropertyChange("globalNamingResources",
oldGlobalNamingResources,
this.globalNamingResources);
}
3.3 解析<Listener>标签
<Listener>标签中定义StandardServer组件中需要的LifecycleListener监听器。<Server>标签内可以设置多个<Listener>。
//解析<Server><Listener>标签
/** 解析<Listener>标签实例化标签中className属性定义的对象,并push到操作栈中 **/
digester.addObjectCreate("Server/Listener",
null, // MUST be specified in the element
"className");
/** 解析<Listener>标签将标签中属性值映射到其实例化对象中**/
digester.addSetProperties("Server/Listener");
/** 解析</Listener>标签将操作栈栈顶对象作为次栈顶对象StandardServer.addLifecycleListener方法调用的参数,设置到StandardServer属性中**/
digester.addSetNext("Server/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
Server中定义的Listener
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
添加LifecycleListener监听器
/**
* 给当前组件添加一个生命周期监听器
*/
@Override
public void addLifecycleListener(LifecycleListener listener) {
lifecycleListeners.add(listener);
}
3.4 解析<Service>属性
<Service>标签中定义StandardServer组件中子组件Service。<Server>标签内可以设置多个<Service>。
//解析<Server><Service>标签
/** 解析<Service>标签实例化StandardService对象,并push到操作栈中 **/
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
/** 解析<Service>标签将标签中属性值映射到StandardService对象中**/
digester.addSetProperties("Server/Service");
/** 解析</Service>标签将操作栈栈顶对象作为次栈顶对象StandardServer.addService方法调用的参数,设置到StandardServer属性中**/
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");
添加Service子组件
/**
* 将service子组件加到Tomcat Server组件中
*/
@Override
public void addService(Service service) {
/** service 反向关联外部 Server组件 **/
service.setServer(this);
synchronized (servicesLock) {
/** 将service组件添加到Server.ervices数组类型属性中 **/
Service results[] = new Service[services.length + 1];
System.arraycopy(services, 0, results, 0, services.length);
results[services.length] = service;
services = results;
/** 如果当前Server组件已经启动,则启动添加Service组件 **/
if (getState().isAvailable()) {
try {
service.start();
} catch (LifecycleException e) {
// Ignore
}
}
/** 将service属性更改通知给监听器 **/
support.firePropertyChange("service", null, service);
}
}
4 实现一键启动
4.1 组件生命周期
StandardServer作为tomcat最上层的组件,和其他所有组件一样都实现了Lifecycle 接口。
public interface Lifecycle {
....
// 初始化方法
public void init() throws LifecycleException;
// 启动方法
public void start() throws LifecycleException;
// 停止方法,和start对应
public void stop() throws LifecycleException;
// 销毁方法,和init对应
public void destroy() throws LifecycleException;
// 获取生命周期状态
public LifecycleState getState();
// 获取字符串类型的生命周期状态
public String getStateName();
}
Tomcat 定义一个基类LifecycleBase 来实现 Lifecycle 接口,把一些公共的逻辑放到基类中去,比如生命状态的转变与维护、生命事件的触发以及监听器的添加和删除等,而子类就负责实现自己的初始化、启动和停止等模板方法。为了避免跟基类中的方法同名,我们把具体子类的实现方法改个名字,在后面加上 Internal,叫 initInternal、startInternal 等。
/**
* 组件初始化动作,所有组件通用操作
* 1 检查校验当前组件状态是否能够初始化
* 2 修改当前的状态从 NEW-->INITIALIZING
* 3 调用每个组件模板方法实现完成初始化动作
* 4 修改当前的状态从 INITIALIZING-->INITIALIZED
*/
@Override
public final synchronized void init() throws LifecycleException {
/** 非NEW状态,不允许调用init()方法 **/
if (!state.equals(LifecycleState.NEW)) {
/** 从sm获取"lifecycleBase.invalidTransition"属性对应日志格式,抛出LifecycleException异常 **/
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
/** 初始化逻辑之前,将状态变更为`INITIALIZING` **/
setStateInternal(LifecycleState.INITIALIZING, null, false);
/** 初始化组件,该方法为一个abstract模板方法,需要组件自行实现 **/
initInternal();
/** 初始化完成之后,状态变更为`INITIALIZED` **/
setStateInternal(LifecycleState.INITIALIZED, null, false);
}
/** 初始化的过程中,可能会有异常抛出,这时需要捕获异常,并将状态变更为`FAILED` **/
catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
/**
* 初始化模板方法
*/
protected abstract void initInternal() throws LifecycleException;
其他模板方法
/**
* 启动模板方法
*/
protected abstract void startInternal() throws LifecycleException;
/**
* 停止模板方法
*/
protected abstract void stopInternal() throws LifecycleException;
/**
* 销毁模板方法
*/
protected abstract void destroyInternal() throws LifecycleException;
4.2 初始化StandardServer组件
StandardServer组件初始化的核心是调用所有Service子组件初始化方法init。
详细流程如下:
-
1 将StringCache类型对象注册到JMX bean中
-
2 将MBeanFactory类型对象注册到JMX bean中
-
3 JNDI服务初始化
-
4 读取Shared类加载器 管理的jar文件,将包含MANIFEST的JAR文件,添加到容器的清单资源中
-
5 调用所有Service子组件初始化方法init
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
//1 将StringCache类型对象注册到JMX bean中
onameStringCache = register(new StringCache(), "type=StringCache");
//2 将MBeanFactory类型对象注册到JMX bean中
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
/** 3 JNDI服务初始化 **/
globalNamingResources.init();
/** 4 读取Shared类加载器 管理的jar文件,将包含MANIFEST的JAR文件,添加到容器的清单资源中 **/
if (getCatalina() != null) {
/** 获取Shared类加载器 **/
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 e) {
// Ignore
} catch (IOException e) {
// Ignore
}
}
}
}
cl = cl.getParent();
}
}
/** 5 调用所有Service子组件初始化方法init **/
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
4.3 启动StandardServer组件
启动start的核心是调用所有Service子组件初始化方法start。
详细流程如下:
-
1 通知LifecycleListener监听器当前组件触发 CONFIGURE_START_EVENT事件
-
2 更正当前组件状态为STARTING
-
3 启动JNDI服务
-
4 调用所有Service子组件启动方法start
/**
* 组件启动模板方法实现
*/
@Override
protected void startInternal() throws LifecycleException {
/** 通知监听器当前组件触发 CONFIGURE_START_EVENT事件 **/
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
/** 更正当前组件状态为STARTING **/
setState(LifecycleState.STARTING);
/** 启动JNDI服务 **/
globalNamingResources.start();
/** 启动所有service组件 **/
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}
}
4.4 停止StandardServer组件
启动stop的核心是调用所有Service子组件初始化方法stop。
详细流程如下:
-
1 通知LifecycleListener监听器当前组件触发 CONFIGURE_STOP_EVENT事件
-
2 更正当前组件状态为STOPPING
-
3 关闭JNDI服务
-
4 调用所有Service子组件启动方法stop
-
5 调用 stopAwait
- 1 设置stopAwait标识为true,stopAwait用来判断tomcat主线程是否要退出
- 2 关闭Socket服务,不在监听shutdown命令
/**
* 组件停止模板方法实现
*/
@Override
protected void stopInternal() throws LifecycleException {
/** 通知监听器当前组件触发 CONFIGURE_STOP_EVENT事件 **/
fireLifecycleEvent(CONFIGURE_STOP_EVENT, null);
/** 更正当前组件状态为STOPPING **/
setState(LifecycleState.STOPPING);
/** 关闭所有service组件 **/
for (int i = 0; i < services.length; i++) {
services[i].stop();
}
/** 关闭JNDI服务 **/
globalNamingResources.stop();
/** 停止监听 shutdown命令 Socket服务 **/
stopAwait();
}
4.5 销毁StandardServer组件
销毁destroy的核心是调用所有Service子组件初始化方法destroy。
1 调用所有Service子组件启动方法destroy
2 销毁JND全局资源
3 jmx bean注销MBeanFactory
4 jmx bean注销StringCache
5 调用LifecycleMBeanBase.destroyInternal 将当前组件对象从jmx 注销
/**
* 组件销毁模板方法实现
*/
@Override
protected void destroyInternal() throws LifecycleException {
/** 调用所有Service子组件启动方法destroy **/
for (int i = 0; i < services.length; i++) {
services[i].destroy();
}
/** 销毁JND全局资源 **/
globalNamingResources.destroy();
/** jmx bean注销MBeanFactory **/
unregister(onameMBeanFactory);
/** jmx bean注销StringCache **/
unregister(onameStringCache);
/** 调用LifecycleMBeanBase.destroyInternal
* 将当前组件对象从jmx 注销
*/
super.destroyInternal();
}
4.6 StandardServer实现一键启动
在这样的设计中,在父组件的 init 方法里需要创建子组件并调用子组件的 init 方法。同样,在父组件的 start 方法里也需要调用子组件的 start 方法。只要调用最顶层组件StandardServer的 init 和 start 方法,整个 Tomcat 就被启动起来了。只要调用最顶层组件StandardServer的 destroy 和 stop 方法,整个 Tomcat 就被关闭。
image5 实现子组件Service管理
5.1 addService
添加已在解析server.xml初始化设置调用
/**
* 将service子组件加到Tomcat Server组件中
*/
@Override
public void addService(Service service) {
/** service 反向关联外部 Server组件 **/
service.setServer(this);
synchronized (servicesLock) {
/** 将service组件添加到Server.ervices数组类型属性中 **/
Service results[] = new Service[services.length + 1];
System.arraycopy(services, 0, results, 0, services.length);
results[services.length] = service;
services = results;
/** 如果当前Server组件已经启动,则启动添加Service组件 **/
if (getState().isAvailable()) {
try {
service.start();
} catch (LifecycleException e) {
// Ignore
}
}
/** 将service属性更改通知给监听器 **/
support.firePropertyChange("service", null, service);
}
}
5.2 返回指定名称Service组件
@Override
public Service findService(String name) {
if (name == null) {
return (null);
}
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
if (name.equals(services[i].getName())) {
return (services[i]);
}
}
}
return (null);
}
5.3 返回Server组件所有Service子组件
/**
* 返回Server组件所有Service子组件
*/
@Override
public Service[] findServices() {
return services;
}
5.4 从Server组件中删除Service子组件
/**
* 从Server组件中删除Service子组件
*/
@Override
public void removeService(Service service) {
synchronized (servicesLock) {
/** 从Service子组件数组找到删除 service 子组件 **/
int j = -1;
for (int i = 0; i < services.length; i++) {
if (service == services[i]) {
j = i;
break;
}
}
/** 没有找到忽略此动作 **/
if (j < 0)
return;
/** 对删除service子组件 停止动作 **/
try {
services[j].stop();
} catch (LifecycleException e) {
// Ignore
}
/** 对service 数组中在删除service子组件后service子组件在数组中前移 **/
int k = 0;
Service results[] = new Service[services.length - 1];
for (int i = 0; i < services.length; i++) {
if (i != j)
results[k++] = services[i];
}
services = results;
/** support通知service属性变更 **/
support.firePropertyChange("service", service, null);
}
}
6 阻塞tomcat主线程
image阻塞tomcat主线程,只要stopAwait不为true, tomcat主线程在此无限循环.监听到客户端发起SHUTDOWN命令后退出
@Override
public void await() {
if( port == -2 ) {
return;
}
if( port==-1 ) {
try {
awaitThread = Thread.currentThread();
while(!stopAwait) {
try {
Thread.sleep( 10000 );
} catch( InterruptedException ex ) {
// continue and check the flag
}
}
} finally {
awaitThread = null;
}
return;
}
/** 创建服务端监听shutdown命令 Socket **/
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();
/** 只要stopAwait不为true, tomcat主线程在此无限循环.监听到客户端发起SHUTDOWN命令后退出 **/
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) {
break;
}
log.error("StandardServer.await: accept: ", e);
break;
}
/** 发生指令的字符数大于1024,则最大读取字符扩容到
* expected += (random.nextInt() % 1024)
*/
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("StandardServer.await: read: ", e);
ch = -1;
}
/** 遍历到控制字符或EOF(-1)终止读取 **/
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
}
}
/** 发生执行是否为 shutdown指令字符串相同,相同则跳出循环Tomcat主线程退出**/
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
}
}
}
}
网友评论