tomcat8 源碼分析 | 組件及啟動過程


tomcat 8 源碼分析 ,本文主要講解tomcat擁有哪些組件,容器,又是如何啟動的

推薦訪問我的個人網站,排版更好看呦: https://chenmingyu.top/tomcat-source-code/

tomcat

簡介

​ Tomcat是Apache 軟件基金會(Apache Software Foundation)的Jakarta 項目中的一個核心項目,Tomcat服務器是一個免費的開放源代碼的Web 應用服務器,屬於輕量級應用服務器。

整體架構

首先我們先看一張圖

在這里插入圖片描述
看上圖總結一下tomcat的組件主要包括:

  • server:整個servlet容器,一個tomcat對應一個server,一個server包含多個service

    server在tomcat中的實現類是:StandardServer

  • service: 一個service包含多個connector(接受請求的協議),和一個container(容器)

    多個connector共享一個container容器,

    service在tomcat中的實現類是:StandardService

  • connector:鏈接器,負責處理客戶端請求,解析不同協議及io方式

  • executor:線程池

  • container:包含engine,host,context,wrapper等組件

  • engine:servlet引擎,container容器中頂層的容器對象,一個engine可以包含多個host主機

    engine在tomcat中的實現類是:StandardEngine

  • host:engine容器的子容器,一個host對應一個網絡域名,一個host包含多個context

    host在tomcat中的實現類是:StandardHost

  • context:host容器的子容器,表示一個web應用

    context在tomcat中的實現類是:StandardContext

  • wrapper:tomcat中最小的容器單元,表示web應用中的servlet

    wrapper在tomcat中的實現類是:StandardWrapper

所以tomcat的組件結構大概是這個樣子的:

在這里插入圖片描述

生命周期:Lifecycle

tomcat的啟動過程非常規范,使用Lifecycle接口統一管理各組件的生命周期,根據各個組件之間的父子級關系,首先調用init()方法逐級初始化各組件,然后在調用start()的方法進行啟動;

Lifecycle接口提供的方法如下,提供了init,start,destory等方法:

在這里插入圖片描述

tomcat中的組件基本都繼承了LifecycleMBeanBase類,LifecycleMBeanBase集成LifecycleBase,LifecycleBase實現Lifecycle接口:

在這里插入圖片描述

LifecycleBase重寫Lifecycle接口,比如init()方法,在init()方法中調用initInternal()方法,initInternal()方法是抽象方法,具體實現交由各個子類(組件)去實現。如果沒有實現initInternal()方法,則調用默認的LifecycleMBeanBase的initInternal方法。

啟動過程

接下來從源碼看一下tomcat的啟動流程:

bootstrap

tomcat的入口類為BootStrap的main方法

Bootstrap中main()方法如下,不重要的代碼省略了

/**
 * Main method and entry point when starting Tomcat via the provided
 * scripts.
 *
 * @param args Command line arguments to be processed
 */
public static void main(String args[]) {
        .....
        //初始化    
        bootstrap.init();
        .....
        if (command.equals("startd")) {
            args[args.length - 1] = "start";
            //實例化各組件 調用Catalina類的load方法
            daemon.load(args);
            //啟動各組件 調用Catalina類的start方法
            daemon.start();
        }
		.....
}

bootstrap.init()的工作是初始化Bootstrap類,包含初始化類加載器

/**
 * Initialize daemon.
 * @throws Exception Fatal initialization error
 */
public void init() throws Exception {
    //初始化類加載
    initClassLoaders();
    ......
	//實例化Catalina類
    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    Object startupInstance = startupClass.getConstructor().newInstance();
   	......
    catalinaDaemon = startupInstance;

}
Catalina

接着調用剛初始化的Catalina類的實例catalinaDaemon的load()方法,重要的就兩點

/**
 * Start a new server instance.
 */
public void load() {
    .....
    // Digester... 實例化組件
    Digester digester = createStartDigester();
	.....加載server.xml......
    file = configFile();
    inputStream = new FileInputStream(file);
    inputSource = new InputSource(file.toURI().toURL().toString());
    ......
	// 初始化sever
    getServer().init();
}
Digester

Digester是一種將xml轉化為java對象的事件驅動型工具,通過讀取xml文件,當識別到特定的節點的時候會執行特定的動作,創建java對象或者執行對象的某個方法

在這里插入圖片描述

通過Digester去創建了Catania中的大量初始化工作,具體詳見源碼:

// 創建server實例
digester.addObjectCreate("Server",
                         "org.apache.catalina.core.StandardServer",
                         "className");
//創建Executor
digester.addObjectCreate("Server/Service/Executor",
                         "org.apache.catalina.core.StandardThreadExecutor",
                         "className");
...等等大量初始化工作...

接着講,getServer().init()方法的作用是初始化Sever,調用LifecycleBase的init()方法,在init方法中調用的是StandardServer類initInternal()方法

StandardServer

StandardServer類圖如下:

jiegou

StandardServer類initInternal()方法:

/**
 * Invoke a pre-startup initialization. This is used to allow connectors
 * to bind to restricted ports under Unix operating environments.
 */
@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();
	...省略很多,但是主要的在下面...
    // Initialize our defined Services
    for (int i = 0; i < services.length; i++) {
        //調用services的init
        services[i].init();
    }
}

前面的時候講過一個server初始化多個services;

StandardService

services[i].init();初始化的是StandardService類,類圖如下

在這里插入圖片描述

StandardService的initInternal() 方法的工作是初始化engine組件,初始化線程池,初始化mapperListener,初始化connector

/**
 * Invoke a pre-startup initialization. This is used to allow connectors
 * to bind to restricted ports under Unix operating environments.
 */
@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();
	//初始化engine
    engine.init();
    //初始化線程池
    // Initialize any Executors
    for (Executor executor : findExecutors()) {
        if (executor instanceof JmxEnabled) {
            ((JmxEnabled) executor).setDomain(getDomain());
        }
        executor.init();
    }
    //初始化mapperListener
    // Initialize mapper listener
    mapperListener.init();
	//初始化connector
    connector.init();
     
}

初始化executor,mapperListener,connector后面再講其作用,先接初始化engine

StandardEngine

StandardEngine的類圖如下:

在這里插入圖片描述

在StandardEngine的初始化中並沒有直接調用host的初始化,而是調用的父類containerBase的initInternal的方法:

//StandardEngine
@Override
protected void initInternal() throws LifecycleException {
    // Ensure that a Realm is present before any attempt is made to start
    // one. This will create the default NullRealm if necessary.
    getRealm();
    super.initInternal();
}
//containerBase
 @Override
 protected void initInternal() throws LifecycleException {
        BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
        startStopExecutor = new ThreadPoolExecutor(
                getStartStopThreadsInternal(),
                getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
                startStopQueue,
                new StartStopThreadFactory(getName() + "-startStop-"));
        startStopExecutor.allowCoreThreadTimeOut(true);
        super.initInternal();
    }

host的init是在start階段去做的,所以后面再說

executor

executor.init();默認調用LifecycleMBeanBase的initInternal方法

mapperListener

mapperListener.init();也默認調用LifecycleMBeanBase的initInternal方法

connector

connector的初始化調用Connector類的initInternal方法,主要是new了一個CoyoteAdapter,初始化protocolHandler

@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    // 實例化 CoyoteAdapter 適配器
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);
	......
    try {
        //初始化 protocolHandler
        protocolHandler.init();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
    }
}

ProtocolHandler.init();的實現:
在這里插入圖片描述

AbstractProtocol是調用endpoint的init方法,這個方法中調用bind()

@Override
public void init() throws Exception {
   //初始化endpoint
   endpoint.init();
}

bind()針對不同的io類型提供了三種的默認實現

在這里插入圖片描述

進入NioEndpoint類的bind()

/**
 * Initialize the endpoint.
 */
@Override
public void bind() throws Exception {
	//使用nio方式監聽端口
    if (!getUseInheritedChannel()) {
        serverSock = ServerSocketChannel.open();
        socketProperties.setProperties(serverSock.socket());
        InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
        serverSock.socket().bind(addr,getAcceptCount());
    } 
    //設置非阻塞
    serverSock.configureBlocking(true); //mimic APR behavior
	......
    //開啟selectorPool
    selectorPool.open();
}
start過程

tomcat的start階段與init階段相似,都是逐層調用,稍有不同的是在於engine,host,context,wrapper的啟動方式;

首先回到Bootstrap的main方法中,繼續執行Catalina類的start(),在start()方法中調getServer().start();

調用LifecycleBase類的start()方法,在這個方法中調動StandardServer類實現的startInternal(),在這個類中繼續調用service的star()方法,以此類推逐層start調用,直到調用engine的start(),我們看下engine的start()方法,在看下StandardEngine的類圖:

在這里插入圖片描述

StandardEngine的startInternal()調用ContainerBase的startInternal()

/**
 * Start this component and implement the requirements
 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
protected synchronized void startInternal() throws LifecycleException {

 	......
    // Start our child containers, if any
    Container children[] = findChildren();
    List<Future<Void>> results = new ArrayList<>();
    for (int i = 0; i < children.length; i++) {
        results.add(startStopExecutor.submit(new StartChild(children[i])));
    }
	......
}

findChildren()的方法找到的是engine容器的子容器然后在new StartChild(children[i])中調用子類容器的start();使用這種方式依次啟動子容器


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM