Tomcat源码系列文章
Tomcat源码解析(二):Bootstrap和Catalina
Tomcat源码解析(四):StandardServer和StandardService
前言
前文Tomcat源码解析(二):Bootstrap和Catalina介绍Tomcat的启动类的加载,在Catalina初始化时加载了server.xml
,创建Server
、Service
、Connector
等一些列组件,然后调用Server
的init
和start
方法,启动tomcat
。Tomcat源码解析(三):LifeCycle生命周期管理介绍了组件init、start、stop等共同生命周期方法,使用模板方法设计模式,具体的实现类由子类去实现。
一、StandardServer
1、StandardServer实例化
- 一个
Server类
的实例就代表了一个Tomcat的容器
,一个Tomcat进程只会有一个Server实例
1.1、Server接口
Server(实现类StandardServer)类图如下,只需要关注左边部分即可。
右边部分为jmx内容,tomcat允许我们使用jmx对tomcat进行监控、管理,截图如下。以后有机会出文章单独讲,所以此篇文章涉及Jmx接口内容就不细讲了。
1.2、解析server.xml
- 通过解析server.xml
实例化StandardServer
,并设置server.xml文件中定义的属性初始化
<!-- 1.Server 相关
Server的主要任务,就是提供一个接口让客户端能访问到这个Service集合,同时维护它所包含的所有的Service的生命周期
port:指定一个端口,这个端口负责监听关闭Tomcat的请求
shutdown:向以上端口发送的关闭服务器的命令字符串
-->
<Server port="8005" shutdown="SHUTDOWN">
<!-- 2.Listener 相关 -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" />
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener"/>
<!-- 3.GlobalNamingResources 相关 -->
<GlobalNamingResources>
<Environment name="simpleValue" type="java.lang.Integer" value="30"/>
<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>
<!-- 4.service 相关 -->
<Service name="Catalina">
...
</Service>
</Server>
1.3、解析<Server>标签
- <Server>标签内容用来实例化StandardServer组件
# Catalina#createStartDigester方法
/** 解析<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"
- 将<server>标签属性映射到StandardServer对象属性中
/**
* Tomcat shutdown操作,对应字符串指令
*/
private String shutdown = "SHUTDOWN";
/**
* Tomcat ShutDown操作,服务端监听Socket端口号。
*/
private int port = 8005;
/**
* Tomcat ShutDown执行,服务端监听Socket地址。
*/
private String address = "localhost";
- 监听SHUTDOWN命令Socket服务在前面Tomcat源码解析(二):Bootstrap和Catalina文章里面有讲
2、init初始化
初始化方法是在Catlina的load方法中加载server.xml后,调用getServer().init()触发的
2.1、LifecycleBase#init
- StandardServer的
init
方法由父类LifecycleBase
实现 - LifecycleBase使用模板模式将初始化的具体操作留给了每个容器自己实现
@Override
public final synchronized void init() throws LifecycleException {
// 非NEW状态,不允许调用init()方法
if (!state.equals(LifecycleState.NEW)) {
// 抛出异常
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
// 初始化逻辑之前,先将状态变更为`INITIALIZING(初始化过程中状态)`
setStateInternal(LifecycleState.INITIALIZING, null, false);
// 初始化,该方法为一个abstract方法,需要组件自行实现
initInternal();
// 初始化完成之后,状态变更为`INITIALIZED(初始化完成状态)`
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
// 初始化的过程中,可能会有异常抛出,这时需要捕获异常,并将状态变更为`FAILED(异常状态)`
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
// 真正的初始化方法,需要子类实现此方法
protected abstract void initInternal() throws LifecycleException;
2.2、LifecycleMBeanBase#initInternal
- LifecycleBase的父类LifecycleMBeanBase可以看到
initInternal
方法的实现 - 但这些都是jmx的内容,跳过,再往上找父类也就是StandardServer了
@Override
protected void initInternal() throws LifecycleException {
if (oname == null) {
mserver = Registry.getRegistry(null, null).getMBeanServer();
oname = register(this, getObjectNameKeyProperties());
}
}
3.2、StandServer#initInternal
- 核心内容就是最后一行内容,
初始化
当前Server下的所有Services
- Server里的service是在server.xml里定义的,在Catalina解析server.xml的时候初始化,并注入到Server对象里
- JNDI:就是通过配置一些xml文件,方便用户直接调用API使用某些通用的资源(不常用,不做过多介绍了)
@Override
protected void initInternal() throws LifecycleException {
// 调用父类LifecycleMBeanBase中的实现
super.initInternal();
// JNDI服务初始化
globalNamingResources.init();
// 省略jmx注册对象内容
...
// 初始化Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
3、start启动
启动方法是在Catlina的start方法中调用getServer().start()触发的
3.1、LifecycleBase#start
- 和init方法一样,StandardServer的
start
方法也是父类LifecycleBase
实现,具体实现留给子类 - 和init方法不一样,LifecycleBase的父类LifecycleMBeanBase没有startInternal方法的实现
public final synchronized void start() throws LifecycleException {
// `STARTING_PREP启动前`、`STARTING启动中`和`STARTED启动完成时,将忽略start()逻辑
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
return;
}
// `NEW`状态时,执行init()方法
if (state.equals(LifecycleState.NEW)) {
init();
}
// `FAILED`状态时,执行stop()方法
else if (state.equals(LifecycleState.FAILED)) {
stop();
}
// 不是`INITIALIZED初始化完成`和`STOPPED停止完成`时,则说明是非法的操作
else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
// 抛出异常
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
// 一般流程会走到这里,刚走完初始化流程,状态为INITIALIZED(初始化完成状态)
try {
// start前的状态设置
setStateInternal(LifecycleState.STARTING_PREP, null, false);
// start逻辑,抽象方法,由组件自行实现
startInternal();
// start过程中,可能因为某些原因失败,这时需要stop操作
if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// 抛出异常
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
// 设置状态为STARTED(启动完成状态)
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// 异常状态
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
}
}
// 真正的启动方法,需要子类实现此方法
protected abstract void startInternal() throws LifecycleException;
3.2、StandardServer#start
- 更正当前组件状态为STARTING(启动过程中状态)
- 核心内容
启动
所有service
组件
@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、stop停止
停止方法是在Catlina的stop方法中调用getServer().stop()触发的
- StandardServer的
stop
方法也是父类LifecycleBase
实现,具体实现留给子类 - LifecycleBase里面无非就是状态切换,直接进入StandardServer#stopInternal
- 核心内容
关闭
所有service
组件
@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();
}
- 设置stopAwait标识为true,
跳出阻塞
SHUTDOWN的socket服务 - 设置awaitSocket为null,
关闭Socket服务
,不在监听SHUTDOWN命令
public void stopAwait() {
stopAwait=true;
Thread t = awaitThread;
if (t != null) {
ServerSocket s = awaitSocket;
if (s != null) {
awaitSocket = null;
try {
s.close();
} catch (IOException e) {
// Ignored
}
}
t.interrupt();
try {
t.join(1000);
} catch (InterruptedException e) {
// Ignored
}
}
}
5、destroy销毁
销毁方法是在Catlina的stop方法中调用getServer().destroy()触发的
- StandardServer的
destroy
方法也是父类LifecycleBase
实现,具体实现留给子类 - 核心内容调用
所有Service
组件destroy
方法
@Override
protected void destroyInternal() throws LifecycleException {
// 调用所有Service子组件启动方法destroy
for (int i = 0; i < services.length; i++) {
services[i].destroy();
}
// 销毁JND全局资源
globalNamingResources.destroy();
// 省略jmx内容
...
}
二、StandardService
1、StandardService实例化
1.1、Service接口
Service(实现类StandardService)类图如下,只需要关注左边部分即可,右边部分为jmx内容。
1.2、解析server.xml
- 大概可以分为4个部分,service属性、executor属性、connector属性、engine属性
- Service作用是把Connector和Engine组装在一起,对外提供服务
- Connector的作用是从客户端接受请求
- Engine的作用是处理接受进来的请求
- 在Tomcat源码解析(一):Tomcat整体架构讲过,一个Server实例可以包含多个
Service对象(一个容器和多个连接器组合)
- 一个容器就是
Engine
表示顶级容器
,包含Host、Context(web应用)、Wrapper(Servlet) - 多个连接器就是
Connector
,同个容器可以支持不同协议和端口
的部署访问
- 一个容器就是
<!--
每个Service元素只能有一个Engine元素
元素处理在同一个<Service>中所有<Connector>元素接收到的客户请求
-->
<!-- 1. Service
name:Service的名称
-->
<Service name="Catalina">
<!--2. 一个或多个excecutors -->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
<!--
3.Connector
主要作用是接受连接请求,创建Request和Response对象用于和请求端交换数据
然后分配线程让Engine来处理这个请求,并把产生的Request和Response对象传给Engine
-->
<!--
客户端可以通过8080端口使用http协议访问tomcat
protocol属性规定了请求的协议,port规定了请求的端口号
redirectPort表示强制要求https而请求是http时,重定向至端口号为8443的Connector。
-->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" redirectPort="8443" />
<!--
客户端可以通过8009端口使用AJP协议访问Tomcat
AJP协议负责和其他的HTTP服务器(如Apache)建立连接
在把Tomcat与其他HTTP服务器集成时,就需要这个连接器
-->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<!--
4. Engine:作用是处理接受进来的请求
-->
<Engine name="Catalina" defaultHost="localhost">
</Engine>
</Service>
1.3、解析<Service>标签
- 标签内容用来实例化StandardServer组件
- addService将创建的StandardService对象设置到Server对象下的
services
集合
# Catalina#createStartDigester方法
/** 解析<Service>标签实例化StandardService对象 **/
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
/** 解析<Service>标签将标签中属性值映射到StandardService对象中 **/
digester.addSetProperties("Server/Service");
/** 将StandardService对象设置到Server对象的service集合中 **/
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");
2、init初始化
初始化方法是在Server的initInternal方法循环遍历调用services[i].init()触发的
2.1、StandardService#initInternal
- StandardService和StandardServer一样也是继承自LifecycleMBeanBase
- 初始化具体实现留给子类也就是StandardService#initInternal
- 顶级容器engine、请求url映射Mapper、连接器Connector初始化内容后面章节单独讲
@Override
protected void initInternal() throws LifecycleException {
// 父类LifecycleMBeanBase注册jmx相关,跳过
super.initInternal();
// 顶级容器engine初始化
if (engine != null) {
engine.init();
}
// 执行器Executor也就是线程池,与传统线程池最大区别就是为了兼容所有任务,采用无界队列
// 默认没有配置线程池,所以这里默认为空,后续nio相关内容会创建
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// 初始化mapperListener,只是注册JMX,跳过
mapperListener.init();
// 连接器Connector初始化
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
connector.init();
} catch (Exception e) {
...
}
}
}
}
3、start、stop、destroy
start、stop、destroy的实现内容与initInternal类似,内部都是调用顶级容器engine、请求url映射Mapper、连接器Connector的start、stop、destroy方法,后面章节单独讲
总结
- 一个Tomcat进程只有一个Server实例,一个Server实例可以包含多个Service对象
Server
组件通过调用init
和start
方法来启动tomcat
的- 而Server的init方法和start方法则是调用
多个Service
的init
和start
方法 - 而一个Service的init方法和start方法则是调用
一个顶级容器engine、一个请求url映射Mapper、多个执行器Executor、多个连接器Connector
的init
和start
方法