博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
tomcat 解析(五)-Tomcat的核心组成和启动过程
阅读量:5093 次
发布时间:2019-06-13

本文共 5812 字,大约阅读时间需要 19 分钟。

声明:源码版本为Tomcat 6.0.35

前面的文章中介绍了Tomcat的基本配置,每个配置项也基本上对应了Tomcat的组件结构,如果要用一张图来形象展现一下Tomcat组成的话,整个Tomcat的组成可以如下图所示:

Tomcat在接收到用户请求时,将会通过以上组件的协作来给最终用户产生响应。首先是最外层的Server和Service来提供整个运行环境的基础设施,而Connector通过指定的协议和接口来监听用户的请求,在对请求进行必要的处理和解析后将请求的内容传递给对应的容器,经过容器一层层的处理后,生成最终的响应信息,返回给客户端。

         Tomcat的容器通过实现一系列的接口,来统一处理一些生命周期相关的操作,而Engine、Host、Context等容器通过实现Container接口来完成处理请求时统一的模式,具体表现为该类容器内部均有一个Pipeline结构,实际的业务处理都是通过在Pipeline上添加Valve来实现,这样就充分保证整个架构的高度可扩展性。Tomcat核心组件的类图如下图所示:

在介绍请求的处理过程时,将会详细介绍各个组件的作用和处理流程。本文将会主要分析Tomcat的启动流程,介绍涉及到什么组件以及初始化的过程,简单期间将会重点分析HTTP协议所对应Connector启动过程。

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的组件整装待命,等待请求的到来。关于请求的处理,会在下篇文章中介绍。

转载于:https://www.cnblogs.com/Struts-pring/p/4917008.html

你可能感兴趣的文章
数据中心虚拟化技术
查看>>
复习文件操作
查看>>
SQL Server 使用作业设置定时任务之一(转载)
查看>>
第二阶段冲刺-01
查看>>
BZOJ1045 HAOI2008 糖果传递
查看>>
JavaScript 克隆数组
查看>>
eggs
查看>>
python3 生成器与迭代器
查看>>
《Genesis-3D开源游戏引擎完整实例教程-跑酷游戏篇03:暂停游戏》
查看>>
CPU,寄存器,一缓二缓.... RAM ROM 外部存储器等简介
查看>>
git .gitignore 文件不起作用
查看>>
Alan Turing的纪录片观后感
查看>>
IOS--沙盒机制
查看>>
sqlite的坑
查看>>
digitalocean --- How To Install Apache Tomcat 8 on Ubuntu 16.04
查看>>
【题解】[P4178 Tree]
查看>>
Mongo自动备份
查看>>
cer证书签名验证
查看>>
synchronized
查看>>
【深度学习】caffe 中的一些参数介绍
查看>>