tomcat在启动时的重点功能如下:
初始化类加载器:主要初始化CommonLoader、CatalinaLoader以及SharedLoader;
解析配置文件:使用Digester组件解析Tomcat的server.xml,初始化各个组件(包含各个web应用,解析对应的web.xml进行初始化);
初始化连接器:初始化声明的Connector,以指定的协议打开端口,等待请求。
不管是通过命令行启动还是通过Eclipse的WST server UI,Tomcat的启动流程是在org.apache.catalina.startup. Bootstrap类的main方法中开始的,在启动时,这个类的核心代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
static
void
main(String args[]) {
if
(daemon ==
null
) {
daemon =
new
Bootstrap();
//实例化该类的一个实例
try
{
daemon.init();
//进行初始化
}
catch
(Throwable t) {
……;
}
}
try
{
……
//此处略去代码若干行
if
(command.equals(
"start"
)) {
daemon.setAwait(
true
);
daemon.load(args);
//执行load,生成组件实例并初始化
daemon.start();
//启动各个组件
}
……
//此处略去代码若干行
}
|
从以上的代码中,可以看到在Tomcat启动的时候,执行了三个关键方法即init、load、和start。后面的两个方法都是通过反射调用org.apache.catalina.startup.Catalina的同名方法完成的,所以后面在介绍时将会直接转到Catalina的同名方法。首先分析一下Bootstrap的init方法,在该方法中将会初始化一些全局的系统属性、初始化类加载器、通过反射得到Catalina实例,在这里我们重点看一下初始化类加载器的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
private
void
initClassLoaders() {
try
{
commonLoader = createClassLoader(
"common"
,
null
);
if
( commonLoader ==
null
) {
// no config file, default to this loader - we might be in a 'single' env.
commonLoader=
this
.getClass().getClassLoader();
}
catalinaLoader = createClassLoader(
"server"
, commonLoader);
sharedLoader = createClassLoader(
"shared"
, commonLoader);
}
catch
(Throwable t) {
log.error(
"Class loader creation threw exception"
, t);
System.exit(
1
);
}
}
|
在以上的代码总,我们可以看到初始化了三个类加载器,这三个类加载器将会有篇博文进行简单的介绍。
然后我们进入Catalina的load方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public
void
load() {
//……
//初始化Digester组件,定义了解析规则
Digester digester = createStartDigester();
//……中间略去代码若干,主要作用为将server.xml文件转换为输入流
try
{
inputSource.setByteStream(inputStream);
digester.push(
this
);
//通过Digester解析这个文件,在此过程中会初始化各个组件实例及其依赖关系
digester.parse(inputSource);
inputStream.close();
}
catch
(Exception e) {
}
// 调用Server的initialize方法,初始化各个组件
if
(getServer()
instanceof
Lifecycle) {
try
{
getServer().initialize();
}
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);
}
}
}
|
在以上的代码中,关键的任务有两项即使用Digester组件按照给定的规则解析server.xml、调用Server的initialize方法。关于Digester组件的使用,后续会有一篇专门的博文进行讲解,而Server的initialize方法中,会发布事件并调用各个Service的initialize方法,从而级联完成各个组件的初始化。每个组件的初始化都是比较有意思的,但是我们限于篇幅先关注Connector的初始化,这可能是最值得关注的。
Connector的initialize方法,核心代码如下:
1
2
3
4
5
6
7
8
9
10
11
|
public
void
initialize()
throws
LifecycleException{
//该适配器会完成请求的真正处理
adapter =
new
CoyoteAdapter(
this
);
//对于不同的实现,会有不同的ProtocolHandler实现类,我们来看 //Http11Protocol,它用来处理HTTP请求
protocolHandler.setAdapter(adapter);
try
{
protocolHandler.init();
}
catch
(Exception e) {
……
}
}
|
在Http11Protocol的init方法中,核心代码如下:
1
2
3
4
5
6
7
8
9
|
public
void
init()
throws
Exception {
endpoint.setName(getName());
//endpoint为JIoEndpoint的实现类
endpoint.setHandler(cHandler);
try
{
endpoint.init();
//核心代码就是调用 JIoEndpoint的初始化方法
}
catch
(Exception ex) {
……
}
}
|
我们看到最终的初始化方法最终都会调到JIoEndpoint的init方法,网络初始化和对请求的最初处理都是通过该类及其内部类完成的,所以后续的内容将会重点关注此类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
void
init()
throws
Exception {
if
(acceptorThreadCount ==
0
) {
//接受请求的线程数
acceptorThreadCount =
1
;
}
if
(serverSocket ==
null
) {
try
{
if
(address ==
null
) {
//基于特定端口创建一个ServerSocket对象,准备接受请求
serverSocket = serverSocketFactory.createSocket(port, backlog);
}
else
{
serverSocket = serverSocketFactory.createSocket(port, backlog, address);
}
}
catch
(BindException orig) {
……
}
}
}
|
在上面的代码中,我们可以看到此时初始化了一个ServerSocket对象,用来准备接受请求。
如果将其比作赛跑,此时已经到了“各就各位”状态,就等最终的那声“发令枪”了,而Catalina的start方法就是“发令枪”啦:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
void
start() {
if
(getServer() ==
null
) {
load();
}
if
(getServer() ==
null
) {
log.fatal(
"Cannot start server. Server instance is not configured."
);
return
;
}
if
(getServer()
instanceof
Lifecycle) {
try
{
((Lifecycle) getServer()).start();
}
catch
(LifecycleException e) {
log.error(
"Catalina.start: "
, e);
}
}
//……
}
|
此时会调用Server的start方法,这里我们重点还是关注JIoEndpoint的start方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public
void
start()
throws
Exception {
if
(!initialized) {
init();
}
if
(!running) {
running =
true
;
paused =
false
;
if
(executor ==
null
) {
//初始化处理连接的线程,maxThread的默认值为200,这也就是为什么 //说Tomcat只能同时处理200个请求的来历
workers =
new
WorkerStack(maxThreads);
}
for
(
int
i =
0
; i < acceptorThreadCount; i++) {
//初始化接受请求的线程
Thread acceptorThread =
new
Thread(
new
Acceptor(), getName() +
"-Acceptor-"
+ i);
acceptorThread.setPriority(threadPriority);
acceptorThread.setDaemon(daemon);
acceptorThread.start();
}
}
}
|
从以上的代码,可以看到,如果没有在server.xml中声明Executor的话,将会使用内部的一个容量为200的线程池用来后续的请求处理。并且按照参数acceptorThreadCount的设置,初始化线程来接受请求。而Acceptor是真正的幕后英雄,接受请求并分派给处理过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
protected
class
Acceptor
implements
Runnable {
public
void
run() {
while
(running) {
// 接受发送过来的请求
Socket socket = serverSocketFactory.acceptSocket(serverSocket);
serverSocketFactory.initSocket(socket);
//处理这个请求
if
(!processSocket(socket)) {
//关闭连接
try
{
socket.close();
}
catch
(IOException e) {
// Ignore
}
}
}
}
}
|
从这里我们可以看到,Acceptor接受Socket请求,并调用processSocket方法来进行请求的处理。至此,Tomcat的组件整装待命,等待请求的到来。关于请求的处理,会在下篇文章中介绍。