你可能不那麼知道的Tomcat生命周期管理 | 博學谷狂野架構師

来源:https://www.cnblogs.com/jiagooushi/archive/2023/03/22/17244202.html
-Advertisement-
Play Games

Tomcat生命周期管理 各種組件如何統一管理 Tomcat的架構設計是清晰的、模塊化、它擁有很多組件,加入在啟動Tomcat時一個一個組件啟動,很容易遺漏組件,同時還會對後面的動態組件拓展帶來麻煩。如果採用我們傳統的方式的話,組件在啟動過程中如果發生異常,會很難管理,比如你的下一個組件調用了sta ...


Tomcat生命周期管理

img

各種組件如何統一管理

Tomcat的架構設計是清晰的、模塊化、它擁有很多組件,加入在啟動Tomcat時一個一個組件啟動,很容易遺漏組件,同時還會對後面的動態組件拓展帶來麻煩。如果採用我們傳統的方式的話,組件在啟動過程中如果發生異常,會很難管理,比如你的下一個組件調用了start方法,但是如果它的上級組件還沒有start甚至還沒有init的話,Tomcat的啟動會非常難管理,因此,Tomcat的設計者提出一個解決方案:用Lifecycle管理啟動,停止、關閉。

生命周期統一介面

Tomcat內部架構中各個核心組件有包含與被包含關係,例如:Server包含了Service.Service又包含了Container和Connector,這個結構有一點像數據結構中的樹,樹的根結點沒有父節點,其他節點有且僅有一個父節點,每一個父節點有0至多個子節點。所以,我們可以通過父容器啟動它的子容器,這樣只要啟動根容器,就可以把其他所有的容器都啟動,從而達到了統一的啟動,停止、關閉的效果。

所有所有組件有一個統一的介面——Lifecycle,把所有的啟動、停止、關閉、生命周期相關的方法都組織到一起,就可以很方便管理Tomcat各個容器組件的生命周期。

Lifecycle其實就是定義了一些狀態常量和幾個方法,主要方法是init,start,stop三個方法。

例如:Tomcat的Server組件的init負責遍歷調用其包含所有的Service組件的init方法。

註意:Server只是一個介面,實現類為StandardServer,有意思的是,StandardServer沒有init方法,init方法是在哪裡,其實是在它的父類LifecycleBase中,這個類就是統一的生命周期管理。

COPYpublic class StandardService extends LifecycleMBeanBase implements Service
    
public abstract class LifecycleMBeanBase extends LifecycleBase
        implements JmxEnabled

LifecycleBase

COPYpublic abstract class LifecycleBase implements Lifecycle {
      @Override
    public final synchronized void init() throws LifecycleException {
        //這個就是為了防止 組件啟動的順序不對
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }

        try {
            //只列印核心組件
            if(this.getClass().getName().startsWith("org.apache.catalina.core")||this.getClass().getName().startsWith("org.apache.catalina.connector")){
                System.out.println(this.getClass()+"--init()");
            }
            setStateInternal(LifecycleState.INITIALIZING, null, false);
            //調用子類的initInternal方法
            initInternal();
            setStateInternal(LifecycleState.INITIALIZED, null, false);
        } catch (Throwable t) {
            handleSubClassException(t, "lifecycleBase.initFail", toString());
        }
    }
   
}

所以StandardServer最終只會調用到initInternal方法,這個方法會初始化子容器Service的init方法

為什麼LifecycleBase這麼玩,其實很多架構源碼都是這麼玩的,包括JDK的容器源碼都是這麼玩的,一個類,有一個介面,同時抽象一個抽象骨架類,把通用的實現放在抽象骨架類中,這樣設計就方便組件的管理,使用LifecycleBase骨架抽象類,在抽象方法中就可以進行統一的處理。

LifeCycle源碼分析

作用

組件生命周期方法的通用介面。 Catalina組件可以實現此介面(以及它們支持的功能的適當介面),以便提供一致的機制來啟動和停止組件

狀態圖

Tomcat中的事件觸發是通過這些狀態來判定的。

COPY*            start()
 *  -----------------------------
 *  |                           |
 *  | init()                    |
 * NEW -»-- INITIALIZING        |
 * | |           |              |     ------------------«-----------------------
 * | |           |auto          |     |                                        |
 * | |          \|/    start() \|/   \|/     auto          auto         stop() |
 * | |      INITIALIZED --»-- STARTING_PREP --»- STARTING --»- STARTED --»---  |
 * | |         |                                                            |  |
 * | |destroy()|                                                            |  |
 * | --»-----«--    ------------------------«--------------------------------  ^
 * |     |          |                                                          |
 * |     |         \|/          auto                 auto              start() |
 * |     |     STOPPING_PREP ----»---- STOPPING ------»----- STOPPED -----»-----
 * |    \|/                               ^                     |  ^
 * |     |               stop()           |                     |  |
 * |     |       --------------------------                     |  |
 * |     |       |                                              |  |
 * |     |       |    destroy()                       destroy() |  |
 * |     |    FAILED ----»------ DESTROYING ---«-----------------  |
 * |     |                        ^     |                          |
 * |     |     destroy()          |     |auto                      |
 * |     --------»-----------------    \|/                         |
 * |                                 DESTROYED                     |
 * |                                                               |
 * |                            stop()                             |
 * ----»-----------------------------»------------------------------

介面定義

Lifecycle介面統一管理Tomcat生命周期。一共做了4件事:

  • 定義13個string類型常量,用於LifecycleEvent時間的type屬性中,用於區分組件發出的LifecycleEvent事件時的狀態。
  • 定義三個管理監聽器的方法,addLifecycleListener、findLifecycleListeners、removeLifecycleListener。
  • 定義4個生命周期的方法,init、start、stop、destory,用於執行生命周期的各個階段的操作。
  • 定義了獲取當前狀態的兩個方法,getState、getStateName、用於獲取當前的狀態。
COPYpublic interface Lifecycle {
    // 13個狀態常量值
    public static final String BEFORE_INIT_EVENT = "before_init";
    public static final String AFTER_INIT_EVENT = "after_init";
    public static final String START_EVENT = "start";
    public static final String BEFORE_START_EVENT = "before_start";
    public static final String AFTER_START_EVENT = "after_start";
    public static final String STOP_EVENT = "stop";
    public static final String BEFORE_STOP_EVENT = "before_stop";
    public static final String AFTER_STOP_EVENT = "after_stop";
    public static final String AFTER_DESTROY_EVENT = "after_destroy";
    public static final String BEFORE_DESTROY_EVENT = "before_destroy";
    public static final String PERIODIC_EVENT = "periodic";
    public static final String CONFIGURE_START_EVENT = "configure_start";
    public static final String CONFIGURE_STOP_EVENT = "configure_stop";
    // 3個監聽器方法
    public void addLifecycleListener(LifecycleListener listener);
    public LifecycleListener[] findLifecycleListeners();
    public void removeLifecycleListener(LifecycleListener listener);
    // 4個生命周期方法
    public void init() throws LifecycleException;
    public void start() throws LifecycleException;
    public void stop() throws LifecycleException;
    public void destroy() throws LifecycleException;
    // 2個當前狀態方法
    public LifecycleState getState();
    public String getStateName();
}

預設實現類

COPYpublic abstract class LifecycleBase implements Lifecycle {
    // 源組件的當前狀態,不同狀態觸發不同事件
    private volatile LifecycleState state = LifecycleState.NEW;
}

監聽器相關方法

事件監聽器需要三個參與者:

  • 事件對象:用於封裝事件的信息,在事件監聽器介面的同一方法中作為參數使用,繼承自java.util.EventObject類。
  • 事件源:觸發事件的源頭,不同事件源觸發不同事件類型。
  • 事件監聽器:負責監聽事件源發出的事件。實現 java.util.EventListener 介面。
COPY// 用於事件通知的已註冊LifecycleListener列表
private final List<LifecycleListener> lifecycleListeners = 
    new CopyOnWriteArrayList<>();

@Override
public void addLifecycleListener(LifecycleListener listener) {
    lifecycleListeners.add(listener);
}

@Override
public LifecycleListener[] findLifecycleListeners() {
    return lifecycleListeners.toArray(new LifecycleListener[0]);
}

@Override
public void removeLifecycleListener(LifecycleListener listener) {
    lifecycleListeners.remove(listener);
}

// 子類根據當前狀態觸發不同事件,實現不同操作
protected void fireLifecycleEvent(String type, Object data) {
    LifecycleEvent event = new LifecycleEvent(this, type, data);
    for (LifecycleListener listener : lifecycleListeners) {
        listener.lifecycleEvent(event);
    }
}

生命周期方法

LifecycleBase 類是Lifecycle 介面的預設實現,所有實現了生命周期的組件都直接或者間接的繼承自LifecycleBase。

init方法
COPY@Override
public final synchronized void init() throws LifecycleException {
    // 只有 NEW 狀態可以調用
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }
    // 設置 生命周期狀態 -- INITIALIZING,觸發相應事件
    setStateInternal(LifecycleState.INITIALIZING, null, false);
    // 模板方法,由具體子類實現
    initInternal();
    // 執行完成,設置生命周期狀態 -- INITIALIZED,觸發相應事件
    setStateInternal(LifecycleState.INITIALIZED, null, false);
}
Start方法
COPY@Override
public final synchronized void start() throws LifecycleException {
    // 此三種狀態不執行
    if (LifecycleState.STARTING_PREP.equals(state) ||      
        LifecycleState.STARTING.equals(state) ||
        LifecycleState.STARTED.equals(state)) {
        return;
    }
    // NEW 狀態 執行 init 方法
    if (state.equals(LifecycleState.NEW)) {
        init();
    // 啟動失敗,調用 stop 方法
    } else if (state.equals(LifecycleState.FAILED)) {
        stop();
    // 其它狀態,非法操作
    } else if (!state.equals(LifecycleState.INITIALIZED) &&
            !state.equals(LifecycleState.STOPPED)) {
        throw new LifecycleException()
    }
    // 設置啟動狀態為 STARTING_PREP【開始準備】
    setStateInternal(LifecycleState.STARTING_PREP, null, false);
    startInternal();
    // 調用完成後判斷組件啟動狀態
    if (state.equals(LifecycleState.FAILED)) {
        stop();
    } else if (!state.equals(LifecycleState.STARTING)) {
        throw new LifecycleException();
    } else {
        setStateInternal(LifecycleState.STARTED, null, false);
    }
}
stop方法
COPY@Override
public final synchronized void stop() throws LifecycleException {
    // STOPPING_PREP、STOPPING、STOPPED此三種狀態不予執行
    if (LifecycleState.STOPPING_PREP.equals(state) || 
        LifecycleState.STOPPING.equals(state) ||
        LifecycleState.STOPPED.equals(state)) {
        return;
    }
    // 如果狀態為 NEW、修改為 STOPPED
    if (state.equals(LifecycleState.NEW)) {
        state = LifecycleState.STOPPED;
        return;
    }
    // 不為 STARTED、FAILED 2種狀態,拋出異常
    if (!state.equals(LifecycleState.STARTED) && 
        !state.equals(LifecycleState.FAILED)) {
        throw new LifecycleException();
    }
    try {
        // 啟動失敗,觸發事件,否則設置生命周期狀態
        if (state.equals(LifecycleState.FAILED)) {
            fireLifecycleEvent(BEFORE_STOP_EVENT, null);
        } else {
            setStateInternal(LifecycleState.STOPPING_PREP, null, false);
        }
        // 調用模板方法
        stopInternal();
        if (!state.equals(LifecycleState.STOPPING) && 
            !state.equals(LifecycleState.FAILED)) {
            throw new LifecycleException();
        }
        setStateInternal(LifecycleState.STOPPED, null, false);
    } catch (Throwable t) {
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException();
    } finally {
        if (this instanceof Lifecycle.SingleUse) {
            setStateInternal(LifecycleState.STOPPED, null, false);
            destroy();
        }
    }
}
destroy方法
COPY@Override
public final synchronized void destroy() throws LifecycleException {
    // 啟動失敗,先調用 stop 方法
    if (LifecycleState.FAILED.equals(state)) {
        stop();
    }
    // DESTROYING、DESTROYED不執行了
    if (LifecycleState.DESTROYING.equals(state) ||
        LifecycleState.DESTROYED.equals(state)) {
        return;
    }
    // 非法狀態
    if (!state.equals(LifecycleState.STOPPED) &&
        !state.equals(LifecycleState.FAILED) &&
        !state.equals(LifecycleState.NEW) &&
        !state.equals(LifecycleState.INITIALIZED)) {
        throw new LifecycleException();
    }

    try {
        setStateInternal(LifecycleState.DESTROYING, null, false);
        destroyInternal();
        setStateInternal(LifecycleState.DESTROYED, null, false);
    } catch (Throwable t) {
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException();
    }
}
設置狀態方法
COPYprivate synchronized void setStateInternal(LifecycleState state,
        Object data, boolean check) throws LifecycleException {

    // 檢查參數
    if (check) {
        if (state == null) {
            throw new LifecycleException();
            return;
        }
        if (!(state == LifecycleState.FAILED ||
                (this.state == LifecycleState.STARTING_PREP &&
                        state == LifecycleState.STARTING) ||
                (this.state == LifecycleState.STOPPING_PREP &&
                        state == LifecycleState.STOPPING) ||
                (this.state == LifecycleState.FAILED &&
                        state == LifecycleState.STOPPING))) {
            throw new LifecycleException();
        }
    }
    this.state = state;
    String lifecycleEvent = state.getLifecycleEvent();
    if (lifecycleEvent != null) {
        fireLifecycleEvent(lifecycleEvent, data);
    }
}

監聽機制

事件監聽器需要三個參與者:

  • 事件對象—用於封裝事件的信息,在事件監聽器介面的統一方法中作為參數,一般繼承 java.util.EventObjecct類。
  • 事件源—觸發事件對的源頭,不同事件源觸發不同事件。
  • 事件監聽器—負責監聽事件源發出的事件,發生事件時,事件源調用事件監聽器的統一方法處理。監聽器一般實現java.util.EventListener介面。
COPYpublic final class LifecycleEvent extends java.util.EventObject {
    public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {
        super(lifecycle);
        this.type = type;
        this.data = data;
    }
}
COPYpublic interface LifecycleListener {
    public void lifecycleEvent(LifecycleEvent event);
}
COPYpublic class HostConfig implements LifecycleListener {
    @Override
    public void lifecycleEvent(LifecycleEvent event) {
        try {
            host = (Host) event.getLifecycle();
            if (host instanceof StandardHost) {
                setCopyXML(((StandardHost) host).isCopyXML());
                setDeployXML(((StandardHost) host).isDeployXML());
                setUnpackWARs(((StandardHost) host).isUnpackWARs());
                setContextClass(((StandardHost) host).getContextClass());
            }
        } catch (ClassCastException e) {
            return;
        }

        // Process the event that has occurred
        if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
            check();
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
            beforeStart();
        } else if (event.getType().equals(Lifecycle.START_EVENT)) {
            start();
        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
            stop();
        }
    }
}
COPY// LifecycleBase.startInternal
this.state = state;
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
    fireLifecycleEvent(lifecycleEvent, data);
}

protected void fireLifecycleEvent(String type, Object data) {
    LifecycleEvent event = new LifecycleEvent(this, type, data);
    for (LifecycleListener listener : lifecycleListeners) {
        listener.lifecycleEvent(event);
    }
}

模板方法

四個模板方法,由子類具體實現

COPYprotected abstract void initInternal() throws LifecycleException;
protected abstract void startInternal() throws LifecycleException;
protected abstract void stopInternal() throws LifecycleException;
protected abstract void destroyInternal() throws LifecycleException;

總結

通過提供init、start、stop、destory方法,通過相應的模板方法【模板設計模式】,提供組件的統一生命周期的管理、事件調度。

本文由傳智教育博學谷狂野架構師教研團隊發佈。

如果本文對您有幫助,歡迎關註點贊;如果您有任何建議也可留言評論私信,您的支持是我堅持創作的動力。

轉載請註明出處!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • SpringBoot異常處理 1.基本介紹 預設情況下,SpringBoot提供/error處理所有錯誤的映射,也就是說當出現錯誤時,SpringBoot底層會請求轉發到/error這個映射路徑所關聯的頁面或者控制器方法。(預設異常處理機制) 要驗證這個點,我們只需要設置一個攔截器,當每次請求時都在 ...
  • 線程同步 線程安全 要保證線程安全有兩個前提: 程式調用了多線程。 多個線程操作共同的變數 以上兩個條件滿足後,程式就有可能觸犯線程不安全的問題 什麼是線程不安全? 舉例說明:假如一場演唱會需要售賣門票,有三個售票口,A,B,C。它們會同時售票,假如一共只有100張票,那麼當100張票售賣完後,售票 ...
  • 職位一 社招雲計算測試開發工程師 崗位職責: 1、負責雲計算產品的測試設計和測試開發工作,包括計算,存儲,網路等方向; 2、包括但不限於功能、性能、可靠性、用戶體驗等系統性測試; 3、通過技術手段,建設雲計算質量體系,包括產品的功能、性能、質量過程數據,保證產品的穩定性; 4、負責雲計算產品的質量推 ...
  • 前提 小白一個,啥都不會,歡迎指點。 題目 隨機生成10個整數(1-100的範圍),保存到數組,並倒序列印以及求平均值,求最大值和最大值的下標,並查找裡面知否有8。 思路 隨機生成-->採用random(),註意範圍在( 1-100) 。 求取最大值下標插入索引 在再次建立一個索引,以此判斷隨機生成 ...
  • JavaSE:多線程學習 01 初識進程 1.1 Process & Thread 1、首先簡要介紹程式。程式是指令和數據的有序集合,其本身沒有任何運行的含義,只是一個靜態的概念。 2、進程則是執行程式的一次執行過程,是一個動態的概念。是系統資源分配的單位。 3、通常在一個進程中可以包含若幹線程。線 ...
  • Swift 備忘清單 IT寶庫整理的Swift開發速查清單,該清單提供了使用 Swift 的示例,涵蓋 Swift 基礎知識、控制流、類型、結構/類、運算符、函數方法等。入門,為開發人員分享快速參考備忘單。 開發速查表大綱 入門 變數 類型註釋 算術運算符 字元串插值 多行字元串 代碼註釋 組成一個 ...
  • 本文介紹基於Python中ArcPy模塊,對大量柵格遙感影像文件進行批量掩膜與批量重採樣的操作。 首先,我們來明確一下本文的具體需求。現有一個存儲有大量.tif格式遙感影像的文件夾;且其中除了.tif格式的遙感影像文件外,還具有其它格式的文件。 我們希望,依據一個已知的面要素矢量圖層文件,對上述文件 ...
  • aliases: [] tags : " #QA #Java " summary: [POI生成Excel超出的單元格樣式的最大數量] author : [yaenli] notekey: [20230322-100908] 問題現象 使用Apache POI生成Excel時,如果創建的單元格樣式過 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...