深入淺出Tomcat/3 - Tomcat生命週期

張太國發表於2019-02-01

在上面的部分,其實我們已經接觸到Tomcat的生命週期了,接下來我們將仔細討論和學習Tomcat的生命週期的具體實現。

LifeCycle介面

這個LifeCycle介面上面在講解Server和Service時其實已經有所接觸。Tomcat通過org.apache.catalina.LifeCycle介面統一接管Tomcat的生命週期,所有繼承自它的元件(即Component)都需要實現這個介面。

我們先俯視看一看LifeCycle介面的定義。

根據上圖,我們清晰看到上圖包含了4個生命週期的方法:
1. init
2. start
3. stop
4. destroy
這4個方法不用多解釋,很直觀,我們前面也有所接觸。

同時,每個方法又定義了幾個常量字串,用於LifeCycleEvent事件的type屬性,用來描述各個狀態的變化。舉個例子,對於init週期,它包含before_init和 after_init兩個字串,其他類似。

它同時也定義了三個管理監聽器的方法,分別如下:
• addLifeCycleListener
• findLifeCycleListeners
• removeLifeCycleListener
顯而易見,它們用來增加、查詢和刪除生命週期的監聽器。

最後定義了獲取當前狀態資訊,用2個方法實現:
• getState
• getStateName
getState返回的是LifeCycleState,而且LifeCycleState是一個列舉型別。如下

public enum LifecycleState {
    NEW(false, null),
    INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
    INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
    STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
    STARTING(true, Lifecycle.START_EVENT),
    STARTED(true, Lifecycle.AFTER_START_EVENT),
    STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
    STOPPING(false, Lifecycle.STOP_EVENT),
    STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
    DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
    DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
    FAILED(false, null);

我們看到它列舉了Tomcat各個生命週期。

LifeCycleBase類

LifeCycleBase是實現了LifeCycle的抽象類,Tomcat的幾個生命週期的管理都交給它,所以弄清楚這個類的實現,基本上也就明白了Tomcat的生命週期了。

在LifeCycle介面裡,我們提到的4個週期的方法以及3個監聽器的管理方法,最後還有2個獲取狀態資訊的方法,我們接下來逐一分析和了解。

4個生命週期方法

方法init

public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}

try {
setStateInternal(LifecycleState.INITIALIZING, null, false);
initInternal();
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.initFail", toString());
}
}

先判斷當前狀態是不是NEW,如果不是丟擲異常。因為init時,代表的是Tomcat的初始化,所以其狀態需要為NEW的。接下來就是設定狀態為LifecycleState.INITIALIZING,也就是正在初始化,然後呼叫initInternal開始初始化了,初始化完之後設定為LifecycleState.INITIALIZED

這裡需要多說一下invalidTransition這個方法,因為後面很多地方都會用到它。

private void invalidTransition(String type) throws LifecycleException {
String msg = sm.getString("lifecycleBase.invalidTransition", type, toString(), state);
throw new LifecycleException(msg);
}

我們看到invalidTransition方法其實沒做其他事情,就是丟擲了一個異常而已。
同時,也介紹一下setStateInternal這個方法,後面也用的較多。

private synchronized void setStateInternal(LifecycleState state, Object data, boolean check)
throws LifecycleException {

if (log.isDebugEnabled()) {
log.debug(sm.getString("lifecycleBase.setState", this, state));
}

if (check) {
// Must have been triggered by one of the abstract methods (assume
// code in this class is correct)
// null is never a valid state
if (state == null) {
invalidTransition("null");
// Unreachable code - here to stop eclipse complaining about
// a possible NPE further down the method
return;
}

// Any method can transition to failed
// startInternal() permits STARTING_PREP to STARTING
// stopInternal() permits STOPPING_PREP to STOPPING and FAILED to
// STOPPING
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))) {
// No other transition permitted
invalidTransition(state.name());
}
}

this.state = state;
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
fireLifecycleEvent(lifecycleEvent, data);
}
}

變數check是傳進來的,是否要檢查。至於後面的邏輯註釋寫的比較清楚,不符合邏輯的狀態轉化都會報錯,不多解釋。

後面的fireLifeCycleEvent的程式碼如下:

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

也是很簡單,就是讓LifeCycle的事件監聽者觸發事件,至於如何去使用這些event,則是每個監聽者自己的業務邏輯了。這也符合一貫的程式碼實現方法,如果有多個監聽器,然後逐一觸發這些監聽器,這其實是事件監聽最基本的實現方法。

以上就是init的方法實現。

方法start
直接上程式碼

public final synchronized void start() throws LifecycleException {

if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {

if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}

return;
}

if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}

try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal();
if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}

第一步就是檢查狀態的合理性,如果已經在準備或者開始了,直接丟擲已經開始的exception。如果是NEW的話,說明init這一步沒有做,那就初始化一下。
正式開始啟動了,首先需要將狀態設定為LifecycleState.STARTING_PREP,接下呼叫startInternal開始啟動。執行完startInternal後驗證state,如果狀態不對,要麼停止,要麼丟擲異常。

方法destroy
destroy其實呼叫的是stop方法。

方法stop
Stop方法

public final synchronized void stop() throws LifecycleException {

if (LifecycleState.STOPPING_PREP.equals(state) || LifecycleState.STOPPING.equals(state) ||
LifecycleState.STOPPED.equals(state)) {

if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStopped", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStopped", toString()));
}

return;
}

if (state.equals(LifecycleState.NEW)) {
state = LifecycleState.STOPPED;
return;
}

if (!state.equals(LifecycleState.STARTED) && !state.equals(LifecycleState.FAILED)) {
invalidTransition(Lifecycle.BEFORE_STOP_EVENT);
}

try {
if (state.equals(LifecycleState.FAILED)) {
// Don't transition to STOPPING_PREP as that would briefly mark the
// component as available but do ensure the BEFORE_STOP_EVENT is
// fired
fireLifecycleEvent(BEFORE_STOP_EVENT, null);
} else {
setStateInternal(LifecycleState.STOPPING_PREP, null, false);
}

stopInternal();

// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
if (!state.equals(LifecycleState.STOPPING) && !state.equals(LifecycleState.FAILED)) {
invalidTransition(Lifecycle.AFTER_STOP_EVENT);
}

setStateInternal(LifecycleState.STOPPED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.stopFail", toString());
} finally {
if (this instanceof Lifecycle.SingleUse) {
// Complete stop process first
setStateInternal(LifecycleState.STOPPED, null, false);
destroy();
}
}
}

首先,檢查一下當前狀態,如果當前狀態為與STOP相關的幾個狀態,則丟擲已經停止的異常。如果為NEW,說明還沒有初始化,直接將STOPPED的狀態賦值一下即可。如果狀態不是STARTED,是不可以停止的,這個時候直接丟擲異常。將觸發的event給監聽器和前面類似,不多做解釋。

3個關於監聽器的方法

前面介紹到LifeCycleBase有三個方法來管理監聽器:
• addLifeCycleListener
• findLifeCycleListeners
• removeLifeCycleListener
我們看看它們的實現。

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


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


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

程式碼比較簡單了,但是需要注意的是lifecycleListeners是一個CopyOnWriteArrayList。

private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();

關於CopyOnWriteArrayList,它繼承了ArrayList。我們都知道ArrayList不是執行緒安全的,但CopyOnWriteArrayList則是執行緒安全的。

/**
* Creates a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection of initially held elements
* @throws NullPointerException if the specified collection is null
*/
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements;
if (c.getClass() == CopyOnWriteArrayList.class)
elements = ((CopyOnWriteArrayList<?>)c).getArray();
else {
elements = c.toArray();
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elements.getClass() != Object[].class)
elements = Arrays.copyOf(elements, elements.length, Object[].class);
}
setArray(elements);
}

我們可以看到程式碼行

elements = c.toArray();

它其實是Copy一份c,這種開銷是很大的。儘管開銷很大,但是當遍歷操作的數量大大超過可變操作的數量時,這種方法可能比其他替代方法更有效。
同時,在add和remove時,我們看到使用到ReentrantLock來保證執行緒安全。
CopyOnWriteArray比較適用於讀多修改少的情景。在Tomcat這裡,一般來說Listener都是在Server.xml,如果在想增加或刪除Listener,必須重新啟動Tomcat,在這種場景下,其實正好符合讀多寫少這種特徵。

public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
}

/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices). Returns the element that was removed from the list.
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return oldValue;
} finally {
lock.unlock();
}
}

獲取狀態

@Override
public LifecycleState getState() {
return state;
}


/**
* {@inheritDoc}
*/
@Override
public String getStateName() {
return getState().toString();
}

程式碼非常簡單,不介紹了。

 

相關文章