若是想讓一個系統可以對外提供服務,咱們須要建立、組裝並啓動這些組件;在服務中止的時候,咱們還須要釋放資源,銷燬這些組件,所以這是一個動態的過程。也就是說,Tomcat 須要動態地管理這些組件的生命週期。html
在父組件的 init 方法裏須要建立子組件並調用子組件的 init 方法。一樣,在父組件的 start 方法裏也須要調用子組件的 start 方法,所以調用者能夠無差異的調用各組件的 init 方法和 start 方法,這就是組合模式的使用java
組件的 init 和 start 調用是由它的父組件的狀態變化觸發的,上層組件的初始化會觸發子組件的初始化,上層組件的啓動會觸發子組件的啓動,所以咱們把組件的生命週期定義成一個個狀態,把狀態的轉變看做是一個事件。而事件是有監聽器的,在監聽器裏能夠實現一些邏輯,而且監聽器也能夠方便的添加和刪除,這就是典型的觀察者模式git
在LifecycleBase已然使用觀察者模式private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
/** * Allow sub classes to fire {@link Lifecycle} events. * * @param type Event type * @param data Data associated with event. */
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
複製代碼
接口可能有多個實現類,爲了不每一個實現類都去實現一些重複的邏輯。能夠定義一個基類來實現共同的邏輯。而基類中每每會定義一些抽象方法,所謂的抽象方法就是說基類不會去實現這些方法,而是調用這些方法來實現骨架邏輯。抽象方法是留給各個子類去實現的,而且子類必須實現,不然沒法實例github
注意下圖註釋中的描述。數據庫
/** * {@inheritDoc} */
@Override
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(); //骨架代碼中調用的預留的抽象方法,未來每一個子類在使用本方法的時候,就是調用的它本身實現了startInternal
if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}
複製代碼
Connector上的線程池負責處理這個它所接收到的全部請求。一個Connector有一個線程池。其餘的後臺任務也有專門的線程池,不會佔用Connector的線程池。bootstrap
//建立並註冊關閉鉤子
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);//關閉鉤子其實就是一個實現了run方法的線程對象。
}
複製代碼
Tomcat會調用Web應用的代碼來處理請求,可能Web應用代碼阻塞在某個地方。數組
理論上:線程數=((線程阻塞時間 + 線程忙綠時間) / 線程忙碌時間) * cpu核數若是線程始終不阻塞,一直忙碌,會一直佔用一個CPU核,所以能夠直接設置 線程數=CPU核數。可是現實中線程可能會被阻塞,好比等待IO。所以根據上面的公式肯定線程數。那怎麼肯定線程的忙碌時間和阻塞時間?要通過壓測,在代碼中埋點統計.緩存
問題:剛到新公司,看他們把一個tomact的connector的線程池設置成800,這個太誇張了吧,connector的線程池只是用來處理接收的http請求,線程池不會用來處理其餘業務自己的事情,設置再大也只能提升請求的併發,並不能提升系統的響應,讓這個線程池幹其餘的事情,並且線程數過高,線程上下文切換時間也高,反而會下降系統的響應速度吧?我理解是否是對的,老師?還有一個問題就是設置的connector線程數,是tomcat啓動的時候就會初始化這麼多固定的線程仍是這只是一個上限,還有就是若是線程處於空閒狀態,會不會進行上下文切換呢?tomcat
解答:這裏誤解了Connector中的線程池,這個線程池就是用來處理業務的。另外你提到線程數設的過高,會有線程切換的開銷,這是對的。線程數具體設多少,根據具體業務而定,若是你的業務是IO密集型的,好比大量線程等待在數據庫讀寫上,線程數應該設的越高。若是是CPU密集型,徹底沒有阻塞,設成CPU核數就行。800這個數有點高,我猜大家的應用屬於IO密集型。併發
本文是我我的學習了李號雙老師的專欄課程以後,結合專欄文章和老師對同窗答疑整理的學習筆記。僅供分享。更多精彩內容,你們能夠掃描下方二位碼,在極客時間訂閱李號雙老師的《深刻拆解Tomcat & Jetty》。獲取一手學習資料。