本文及后續(xù)幾篇文章開(kāi)始分析容器組件杯道,它們都與請(qǐng)求處理息息相關(guān)谦疾,首先介紹容器組件共同的父類(lèi)Container接口及ContainerBase抽象類(lèi)觅彰。
容器組件
容器組件處理客戶(hù)端請(qǐng)求并返回響應(yīng)对途,Tomcat中的容器組件按從高到低的順序有以下四個(gè)層次:
- Engine:表示整個(gè)Catalina servlet引擎斯够,可以包含一個(gè)或多個(gè)子容器,如Host或者Context等媒楼;
- Host:表示虛擬主機(jī)乐尊,包含Context;
- Context:表示ServletContext划址,包含一個(gè)或多個(gè)Wrapper扔嵌;
- Wrapper:表示單獨(dú)的servlet定義。
需要注意的是夺颤,Catalina不需要包括所有上述容器組件似谁,所以容器組件的實(shí)現(xiàn)必須被設(shè)計(jì)成在沒(méi)有父容器的情況下依然可以正確運(yùn)行被去。
Container接口
Container接口繼承了Lifecycle接口突那,表示Servlet容器相關(guān)組件的抽象雀久,類(lèi)層次結(jié)構(gòu)如下圖所示:
public interface Container extends Lifecycle {
// ----------------------------------------------------- Manifest Constants
public static final String ADD_CHILD_EVENT = "addChild";
public static final String ADD_VALVE_EVENT = "addValve";
public static final String REMOVE_CHILD_EVENT = "removeChild";
public static final String REMOVE_VALVE_EVENT = "removeValve";
// ------------------------------------------------------------- Properties
public Log getLogger();
public String getLogName();
public ObjectName getObjectName();
public String getDomain();
public String getMBeanKeyProperties();
public Pipeline getPipeline();
public Cluster getCluster();
public void setCluster(Cluster cluster);
public int getBackgroundProcessorDelay();
public void setBackgroundProcessorDelay(int delay);
public String getName();
public void setName(String name);
public Container getParent();
public void setParent(Container container);
public ClassLoader getParentClassLoader();
public void setParentClassLoader(ClassLoader parent);
public Realm getRealm();
public void setRealm(Realm realm);
// --------------------------------------------------------- Public Methods
public void backgroundProcess();
public void addChild(Container child);
public void addContainerListener(ContainerListener listener);
public void addPropertyChangeListener(PropertyChangeListener listener);
public Container findChild(String name);
public Container[] findChildren();
public ContainerListener[] findContainerListeners();
public void removeChild(Container child);
public void removeContainerListener(ContainerListener listener);
public void removePropertyChangeListener(PropertyChangeListener listener);
public void fireContainerEvent(String type, Object data);
public void logAccess(Request request, Response response, long time, boolean useDefault);
public AccessLog getAccessLog();
public int getStartStopThreads();
public void setStartStopThreads(int startStopThreads);
public File getCatalinaBase();
public File getCatalinaHome();
}
- 一個(gè)容器可以包含其他容器,可以通過(guò)addChild寥裂、findChild嵌洼、findChildren、removeChild和setParent等接口方法進(jìn)行操作封恰;
- 通過(guò)addContainerListener麻养、findContainerListeners和removeContainerListener等接口方法可以向容器組件添加容器事件監(jiān)聽(tīng)器ContainerListener,還可以通過(guò)fireContainerEvent方法發(fā)布容器事件诺舔;
- 通過(guò)addPropertyChangeListener和removePropertyChangeListener可以向容器組件添加屬性值改變監(jiān)聽(tīng)器PropertyChangeListener鳖昌;
- 注意Container接口繼承了Lifecycle接口,Lifecycle接口添加的是生命周期事件監(jiān)聽(tīng)器LifecycleListener低飒,因此容器組件包含三種監(jiān)聽(tīng)器:生命周期事件監(jiān)聽(tīng)器LifecycleListener许昨、容器事件監(jiān)聽(tīng)器ContainerListener和屬性值改變監(jiān)聽(tīng)器PropertyChangeListener。
ContainerBase抽象類(lèi)
ContainerBase抽象類(lèi)是Container接口的默認(rèn)實(shí)現(xiàn)褥赊,它繼承了LifecycleBase類(lèi)糕档,同時(shí)也是其他容器組件如StandardEngine、StandardHost和StandardContext等的父類(lèi)拌喉,其部分成員變量如下:
public abstract class ContainerBase extends LifecycleMBeanBase
implements Container {
// 省略一些代碼
/**
* The child Containers belonging to this Container, keyed by name.
*/
protected final HashMap<String, Container> children = new HashMap<>();
protected final List<ContainerListener> listeners = new CopyOnWriteArrayList<>();
protected String name = null;
protected Container parent = null;
protected ClassLoader parentClassLoader = null;
protected final Pipeline pipeline = new StandardPipeline(this);
/**
* The number of threads available to process start and stop events for any
* children associated with this container.
*/
private int startStopThreads = 1;
protected ThreadPoolExecutor startStopExecutor;
@Override
public int getStartStopThreads() {
return startStopThreads;
}
/**
* Handles the special values.
*/
private int getStartStopThreadsInternal() {
int result = getStartStopThreads();
// Positive values are unchanged
if (result > 0) {
return result;
}
// Zero == Runtime.getRuntime().availableProcessors()
// -ve == Runtime.getRuntime().availableProcessors() + value
// These two are the same
result = Runtime.getRuntime().availableProcessors() + result;
if (result < 1) {
result = 1;
}
return result;
}
@Override
public void setStartStopThreads(int startStopThreads) {
this.startStopThreads = startStopThreads;
// Use local copies to ensure thread safety
ThreadPoolExecutor executor = startStopExecutor;
if (executor != null) {
int newThreads = getStartStopThreadsInternal();
executor.setMaximumPoolSize(newThreads);
executor.setCorePoolSize(newThreads);
}
}
}
- children變量是一個(gè)以子容器組件名為鍵翼岁、子容器組件自身為值的Map;
- listeners變量保存了添加到該容器組件的容器事件監(jiān)聽(tīng)器司光,使用CopyOnWriteArrayList的原因請(qǐng)參見(jiàn)《Effective Java》第三版第79條;
- name變量是該容器組件的名字悉患;
- parent變量引用該容器組件的父容器組件残家;
- pipeline變量引用一個(gè)StandardPipeline,為什么要有一個(gè)Pipeline呢售躁?從ContainerBase的類(lèi)注釋可以得到答案:子類(lèi)需要將自己對(duì)請(qǐng)求的處理過(guò)程封裝成一個(gè)Valve并通過(guò)Pipeline的setBasic方法安裝到Pipeline坞淮;
- startStopExecutor表示啟動(dòng)和停止子容器組件用的線程池茴晋,startStopThreads表示這個(gè)線程池的線程數(shù)。
- Use local copies to ensure thread safety 這句注釋沒(méi)看明白回窘,即使用了局部變量也是同一個(gè)引用诺擅。
組件初始化
@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();
}
- 初始化過(guò)程為容器組件自己創(chuàng)建了一個(gè)線程池,該線程池用于執(zhí)行子容器的啟動(dòng)和停止等過(guò)程啡直;
- startStopExecutor里的每個(gè)線程的名稱(chēng)是“容器組件名稱(chēng)-startStop-”再加上線程計(jì)數(shù)烁涌,這在Tomcat啟動(dòng)的日志中有所體現(xiàn),如Host組件啟動(dòng)時(shí)日志會(huì)有如“l(fā)ocalhost-startStop-1”字樣的輸出酒觅。
組件啟動(dòng)
@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])));
}
boolean fail = false;
for (Future<Void> result : results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
fail = true;
}
}
if (fail) {
throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"));
}
// Start the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();
setState(LifecycleState.STARTING);
// Start our thread
threadStart();
}
- 啟動(dòng)過(guò)程利用初始化時(shí)創(chuàng)建的線程池啟動(dòng)了各個(gè)子容器撮执,如果startStopThreads大于1那么多個(gè)子容器可以并發(fā)地啟動(dòng)。容器組件啟動(dòng)時(shí)會(huì)等待全部的子容器組件啟動(dòng)完畢舷丹,若有一個(gè)子容器啟動(dòng)失敗就會(huì)拋出異常抒钱;
- 如果有與容器組件關(guān)聯(lián)的Pipeline則啟動(dòng)Pipeline;
- 調(diào)用基類(lèi)LifecycleBase的setState方法發(fā)布LifecycleState.STARTING事件給添加到容器組件自身的生命周期事件監(jiān)聽(tīng)器颜凯。
Pipeline組件
Pipeline組件用于封裝容器組件的請(qǐng)求處理過(guò)程谋币,一般來(lái)說(shuō)每個(gè)容器組件都有與其關(guān)聯(lián)的一個(gè)Pipeline。
Pipeline接口的代碼如下:
public interface Pipeline {
public Valve getBasic();
public void setBasic(Valve valve);
public void addValve(Valve valve);
public Valve[] getValves();
public void removeValve(Valve valve);
public Valve getFirst();
public boolean isAsyncSupported();
public Container getContainer();
public void setContainer(Container container);
public void findNonAsyncValves(Set<String> result);
}
- getBasic症概、setBasic與基本閥(Basic Valve)的概念有關(guān)蕾额,Pipeline是由一系列閥(Valve)組成的鏈表,基本閥始終指向最后一個(gè)閥穴豫;
- 基本閥封裝了容器組件對(duì)請(qǐng)求的處理過(guò)程凡简,總是在一個(gè)Pipeline的末尾執(zhí)行;
- 其余的方法與鏈表操作有關(guān)精肃,如添加秤涩、移除閥等方法。
StandardPipeline類(lèi)繼承LifecycleBase類(lèi)并實(shí)現(xiàn)了Pipeline接口司抱,與閥有關(guān)的方法實(shí)現(xiàn)很簡(jiǎn)單筐眷,在此不再贅述。initInternal方法沒(méi)有做任何事情习柠,startInternal則依次啟動(dòng)了各個(gè)實(shí)現(xiàn)了Lifecycle接口的閥匀谣。
@Override
protected void initInternal() {
// NOOP
}
@Override
protected synchronized void startInternal() throws LifecycleException {
// Start the Valves in our pipeline (including the basic), if any
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle)
((Lifecycle) current).start();
current = current.getNext();
}
setState(LifecycleState.STARTING);
}
- 在看StandardPipeline類(lèi)源碼的過(guò)程中需要注意basic變量永遠(yuǎn)指向最后一個(gè)閥,first指向第一個(gè)閥资溃。當(dāng)只有一個(gè)閥時(shí)武翎,first為null但basic不會(huì)是null,這時(shí)getFirst會(huì)返回basic而不是null溶锭。
Valve組件
Valve組件是容器組件中處理請(qǐng)求的組件宝恶,主要用在Pipeline中,是一種責(zé)任鏈設(shè)計(jì)模式。Tomcat內(nèi)置了很多閥的實(shí)現(xiàn)垫毙,如前文提到的StandardEngineValve霹疫、StandardHostValve和StandardContextValve,具體可參考閥的Valve配置文檔综芥。
Valve接口的碼如下:
public interface Valve {
public Valve getNext();
public void setNext(Valve valve);
public void backgroundProcess();
public void invoke(Request request, Response response)
throws IOException, ServletException;
public boolean isAsyncSupported();
}
- getNext和setNext分別獲取和設(shè)置后繼閥丽蝎;
- invoke方法處理請(qǐng)求,處理規(guī)約可以參考Valve API文檔膀藐。