`
lengyun3566
  • 浏览: 452498 次
  • 性别: Icon_minigender_1
  • 来自: 大连
博客专栏
D59180b9-02f1-3380-840c-ea34da46143c
《Spring Secur...
浏览量:383396
社区版块
存档分类
最新评论

Tomcat源码解读系列(二)——Tomcat的核心组成和启动过程

 
阅读更多

 

声明:源码版本为Tomcat 6.0.35

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



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

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



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

Tomcat在启动时的重点功能如下:

1、初始化类加载器:主要初始化CommonLoaderCatalinaLoader以及SharedLoader

2、解析配置文件:使用Digester组件解析Tomcatserver.xml,初始化各个组件(包含各个web应用,解析对应的web.xml进行初始化);

3、初始化连接器:初始化声明的Connector,以指定的协议打开端口,等待请求。

不管是通过命令行启动还是通过EclipseWST server UITomcat的启动流程是在org.apache.catalina.startup. Bootstrap类的main方法中开始的,在启动时,这个类的核心代码如下所示:

 

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启动的时候,执行了三个关键方法即initload、和start。后面的两个方法都是通过反射调用org.apache.catalina.startup.Catalina的同名方法完成的,所以后面在介绍时将会直接转到Catalina的同名方法。首先分析一下Bootstrapinit方法,在该方法中将会初始化一些全局的系统属性、初始化类加载器、通过反射得到Catalina实例,在这里我们重点看一下初始化类加载器的方法:

 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);
        }
    }

 在以上的代码总,我们可以看到初始化了三个类加载器,这三个类加载器将会有篇博文进行简单的介绍。

然后我们进入Catalinaload方法:

 

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、调用Serverinitialize方法。关于Digester组件的使用,后续会有一篇专门的博文进行讲解,而Serverinitialize方法中,会发布事件并调用各个Serviceinitialize方法,从而级联完成各个组件的初始化。每个组件的初始化都是比较有意思的,但是我们限于篇幅先关注Connector的初始化,这可能是最值得关注的。

Connectorinitialize方法,核心代码如下:

public void initialize() throws LifecycleException{
     //该适配器会完成请求的真正处理   
adapter = new CoyoteAdapter(this);
	//对于不同的实现,会有不同的ProtocolHandler实现类,我们来看	//Http11Protocol,它用来处理HTTP请求
        protocolHandler.setAdapter(adapter);
        try {
            protocolHandler.init();
        } catch (Exception e) {
            ……
        }
    } 

 Http11Protocolinit方法中,核心代码如下:

 

 public void init() throws Exception {

        endpoint.setName(getName());//endpoint为JIoEndpoint的实现类
        endpoint.setHandler(cHandler);
        try {
            endpoint.init();//核心代码就是调用 JIoEndpoint的初始化方法
        } catch (Exception ex) {
           ……
        }
    }

 我们看到最终的初始化方法最终都会调到JIoEndpointinit方法,网络初始化和对请求的最初处理都是通过该类及其内部类完成的,所以后续的内容将会重点关注此类:

 

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对象,用来准备接受请求。

如果将其比作赛跑,此时已经到了“各就各位”状态,就等最终的那声“发令枪”了,而Catalinastart方法就是“发令枪”啦:

 

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);
            }
        }
      //……
 } 

此时会调用Serverstart方法,这里我们重点还是关注JIoEndpointstart方法:

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是真正的幕后英雄,接受请求并分派给处理过程:

 

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

 

 

  • 大小: 49.2 KB
  • 大小: 56.7 KB
3
1
分享到:
评论
3 楼 lei_jiang0814 2014-06-12  
这段时间正在看这方面的额,写的不错 ,谢谢.....
2 楼 zhou363667565 2013-04-02  
讲解的很不错,看了之后受益匪浅.
1 楼 coolhuali 2012-09-03  
/* 联系人对象*/
public class ContactPerson {
private String personName;
private String mobileNumber;
private String email;

public void setMobileNumber(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
public String getMobileNumber() {
return mobileNumber;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
}

/*联系人*/
public class Recipient {
private List<ContactPerson> contactPersonList=new ArrayList();
public void addContactPerson(ContactPerson contactPerson){
this.contactPersonList.add(contactPerson);
}
public Iterator<ContactPerson> iterator(){
return contactPersonList.iterator();
}
}

/*邮件发送接口*/
public interface SMTransport {
public abstract void sendMessage(ShortMessage shortmessage);
}

/*使用手机短信的方式发送消息*/
public class CellPhoneTransport implements SMTransport {
public void sendMessage(ShortMessage shortmessage)
{
Recipient recipient = shortmessage.getRecipient();
Iterator it = recipient.iterator();
while(it.hasNext())
{
ContactPerson cp = it.next();
System.out.println("向手机号为"+cp.getMobileNumber()+"的联系人发送信息,内容:"+shortmessage.getContent());
}
}
}

/*使用邮件的方式发送消息*/
public class MailTransport implements SMTransport {
public void sendEmail(ShortMessage shortmessage)
{
Recipient recipient = shortmessage.getRecipient ();
Iterator it = recipient.iterator();
while(it.next())
{
ContactPerson cp = (ContactPerson)it.next();
System.out.println("向邮件地址为"+cp.getEmail()+"的联系人发送信息,内容:"+shortmessage.getContent());
}
}
}

/* 短消息核心类*/
public class ShortMessage {
private String content;
private Recipient recipient;
private SMTransport smTransport;

public void setRecipient(Recipient recipient){
this.recipient = recipient;
}

public Recipient getRecipient(){
return this.recipient;
}

public void setContent(String content){
this.content = content;
}

public String getContent(){
return this.content;
}

public void addTransport(SMTransport smTransport){
this.smTransport = smTransport;
}

public void send(){
smTransport.sendMessage(this);
}
}

相关推荐

    tomcat源码阅读(一)——环境搭建

    本篇将聚焦于"Tomcat源码阅读(一)——环境搭建",探讨如何搭建一个适合源码学习的开发环境。 首先,我们需要了解Tomcat是什么。Tomcat是一款开源的Java Servlet容器,由Apache软件基金会维护,实现了Java EE中的...

    tomcat 源码分析系列文档

    9. "Tomcat启动源代码分析.pdf":深入到启动脚本和Java代码,解释了从启动脚本开始,如何初始化和启动Tomcat服务的全过程。 10. "tomcat类加载机制.pdf":再次聚焦于Tomcat的类加载机制,可能深入到更多细节和技巧...

    tomcat源码+文档pdf+源码解析

    这个资源包包含了Tomcat的源码、文档以及源码解析,对于深入理解Tomcat的工作原理、优化应用性能以及进行二次开发具有极大的帮助。 首先,让我们深入探讨Tomcat的源码。源码是软件的基石,通过阅读源码,我们可以...

    Ant编译Tomcat源码、MyEclipse导入Tomcat源码、执行Tomcat源码启动Tomcat

    在Apache Tomcat的源码中,通常会有一个名为`build.xml`的Ant构建脚本,用于指导整个构建过程。在编译Tomcat源码之前,确保已安装Java JDK和Ant。下载并解压`apache-tomcat-6.0.37-src.zip`后,进入解压后的目录,...

    Syske#person-learning-note#Tomcat源码分析-壹——启动过程值init方法1

    前言从今天开始,我们开始分析tomcat的源码,至于原因嘛,第一Tomcat是非常优秀的web服务器,它占据着全球一半以上的市场份额,就连spring boot

    02-Tomcat源码解读1

    总的来说,Tomcat源码解读需要把握住Server和Service这两个核心组件,理解它们如何通过Connector与Engine协同工作,以及生命周期管理和启动流程的设计。同时,要关注Container的层次结构以及与配置文件的交互,这将...

    「Tomcat源码剖析」.pdf

    Tomcat源码剖析 : 整体架构 层层分析 源码解析 架构分析 (Http服务器功能:Socket通信(TCP/IP)、解析Http报文 Servlet容器功能:有很多Servlet(自带系统级Servlet+自定义Servlet),Servlet处理具体的业务逻辑...

    tomcat源码解析

    - **第6章**:解释了Tomcat中的生命周期管理机制,包括如何控制组件的启动和关闭过程。 #### 第1章 一个简单的Web服务器 ##### The Hypertext Transfer Protocol (HTTP) HTTP是超文本传输协议的简称,是一种应用...

    tomcat源码

    1. **Catalina**: 这是Tomcat的核心组件,负责处理Servlet和JSP的生命周期,包括加载Web应用、管理Servlet容器、处理请求和响应等。其中`org.apache.catalina`包下的类是Catalina的核心。 2. ** Coyote**: 这是...

    tomcat工作原理深入详解——HowTomcatWorks中文版.pdf

    tomcat工作原理深入详解——HowTomcatWorks中文版.pdf

    TOMCAT源码分析(启动框架)

    【TOMCAT源码分析(启动框架)】 Tomcat是一款广泛应用的开源Java Servlet容器,它实现了Java Servlet和JavaServer Pages(JSP)规范,为Web应用程序提供了运行环境。本篇文章将深入探讨Tomcat的系统框架及其启动流程...

    tomcat源码导入myeclipse

    【标题】"Tomcat源码导入MyEclipse"是一个针对Java开发者的重要实践操作,它涉及到两个关键组件:Tomcat服务器和MyEclipse集成开发环境。Tomcat是Apache软件基金会的一个开源项目,它作为Servlet和JavaServer Pages...

    编译tomcat源码所需jar包

    本文将深入探讨“编译Tomcat源码所需jar包”这一主题,帮助开发者了解如何从源码构建Tomcat,以及在这个过程中需要用到的关键jar包。 首先,我们来了解一下为什么要从源码编译Tomcat。直接下载预编译的二进制版本...

    tomcat7源码

    每个组件都有自己的启动和停止方法,这些方法在`Lifecycle`接口中定义。通过观察源码,我们可以了解到如何正确初始化和关闭Tomcat的各种组件。 此外,源码中还有许多值得关注的部分,如会话管理(SessionManager)...

    tomcat源码资源包

    Tomcat源码的主要组成部分包括以下几个关键模块: 1. **Catalina**:这是Tomcat的核心组件,负责处理Servlet容器的大部分工作,如加载Web应用程序、管理Servlet实例、处理请求和响应等。 2. **Coyote**:负责处理...

    tomcat7源码下载

    二、Tomcat7启动流程 1. Bootstrap:启动过程始于Bootstrap类,它加载并初始化Server对象。 2. Server:Server对象包含了全局配置信息,并管理Service组件。 3. Service:Service包含一个或多个Connector(如...

    tomcat8源码

    Tomcat中的组件都遵循生命周期接口,包括初始化、启动、停止和销毁四个阶段。这些接口使得开发者可以在各个阶段添加自定义逻辑。 4. **类加载机制** Tomcat使用自定义的类加载器来加载Web应用中的类,以实现不同...

Global site tag (gtag.js) - Google Analytics