前言
回顧【深入理解Tomcat(三)架構(gòu)及組件】察净,同時(shí)參考tomcat自帶的server.xml
诚撵,我們發(fā)現(xiàn)在連接器(Connector)和容器(Container)外層主要是Server
和Service
。基于由外而內(nèi)互亮、由淺入深的方式,本文先來分析Server
和Service
余素。
這兒豹休,我們在【深入理解Tomcat(三)架構(gòu)及組件】之上增加一張更為完整的整體結(jié)構(gòu)圖!
在【深入理解Tomcat(二)Lifecycle】中桨吊,我們分析了Lifecycle威根,也知道了每個(gè)tomcat組件都有init()
、start()
等生命周期方法视乐。因?yàn)長ifecycle是基于模板方法模式設(shè)計(jì)的洛搀,init()
會(huì)調(diào)用initInternal()
,而start()
會(huì)調(diào)用startInternal()
佑淀。為了降低分析源碼的復(fù)雜性留美,我們在對(duì)每個(gè)組件進(jìn)行分析的時(shí)候,只會(huì)關(guān)心生命周期接口下的模板方法伸刃,而不會(huì)重復(fù)分析其下的非模板方法谎砾!
Server
-
Catalina.load()
里面調(diào)用了Server.init()
方法。 -
Catalina.start()
里面調(diào)用了Server.start()
方法捧颅。
所以我們從Server
的init()
和start()
著手分析景图。
在分析之前,我們先看看Server有哪些方法:
/**
* A <code>Server</code> element represents the entire Catalina
* servlet container. Its attributes represent the characteristics of
* the servlet container as a whole. A <code>Server</code> may contain
* one or more <code>Services</code>, and the top level set of naming
* resources.
* <p>
* Normally, an implementation of this interface will also implement
* <code>Lifecycle</code>, such that when the <code>start()</code> and
* <code>stop()</code> methods are called, all of the defined
* <code>Services</code> are also started or stopped.
* <p>
* In between, the implementation must open a server socket on the port number
* specified by the <code>port</code> property. When a connection is accepted,
* the first line is read and compared with the specified shutdown command.
* If the command matches, shutdown of the server is initiated.
* <p>
* <strong>NOTE</strong> - The concrete implementation of this class should
* register the (singleton) instance with the <code>ServerFactory</code>
* class in its constructor(s).
*
* @author Craig R. McClanahan
*/
public interface Server extends Lifecycle {
// ------------------------------------------------------------- Properties
public NamingResourcesImpl getGlobalNamingResources();
public void setGlobalNamingResources
(NamingResourcesImpl globalNamingResources);
public javax.naming.Context getGlobalNamingContext();
public int getPort();
public void setPort(int port);
public String getAddress();
public void setAddress(String address);
public String getShutdown();
public void setShutdown(String shutdown);
public ClassLoader getParentClassLoader();
public void setParentClassLoader(ClassLoader parent);
public Catalina getCatalina();
public void setCatalina(Catalina catalina);
public File getCatalinaBase();
public void setCatalinaBase(File catalinaBase);
public File getCatalinaHome();
public void setCatalinaHome(File catalinaHome);
// --------------------------------------------------------- Public Methods
public void addService(Service service);
public void await();
public Service findService(String name);
public Service[] findServices();
public void removeService(Service service);
public Object getNamingToken();
}
除了Server
本身包含的方法addService(Service service)
和Service[] findServices()
碉哑,我們額外閱讀并翻譯一下Server的javadoc注釋挚币。
- 一個(gè)
Server
節(jié)點(diǎn)代表整個(gè)Catalina servlet容器。它的屬性代表這個(gè)servlet容器的所有特征谭梗。一個(gè)Server
可以包含1個(gè)或多個(gè)Service
忘晤,以及頂級(jí)名字資源。- 正常情況下激捏,該接口的實(shí)現(xiàn)類也必須實(shí)現(xiàn)
Lifecycle接口
设塔,因此當(dāng)start()
和stop()
方法被調(diào)用的時(shí)候,所有定義在其下面每個(gè)Service
的start()
和stop()
也必須相應(yīng)地得到調(diào)用。- 實(shí)現(xiàn)類必須在指定的端口號(hào)打開一個(gè)
server socket
(服務(wù)器套接字)闰蛔。當(dāng)一個(gè)連接被接受痕钢,且第一行讀取之后,需要比較特殊的shutdown命令
序六,如果命令匹配成功任连,則Server的shutdown操作將被發(fā)起。
Server
的實(shí)現(xiàn)類為StandardServer
例诀,我們分析一下StandardServer.initInternal()
方法随抠。該方法用于對(duì)Server
進(jìn)行初始化,關(guān)鍵的地方就是代碼最后對(duì)services的循環(huán)操作繁涂,對(duì)每個(gè)service調(diào)用init方法拱她。
【注】:這兒我們只粘貼出這部分代碼。
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
初始化完成之后接著會(huì)調(diào)用start()
方法扔罪,還是很簡單秉沼,其實(shí)就是調(diào)用每個(gè)service的start()
方法。
@Override
protected void startInternal() throws LifecycleException {
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
globalNamingResources.start();
// Start our defined Services
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}
}
Service
既然一個(gè)Server可以包含多個(gè)Service矿酵,對(duì)一個(gè)Server
的init()
和start()
操作唬复,其實(shí)就是對(duì)Server
下的每個(gè)Service
進(jìn)行init()
和start()
操作。所以全肮,我們接下來要分析的必然是Service
敞咧。因?yàn)?code>Service的實(shí)現(xiàn)類為StandardService
,所以我們就分析這個(gè)實(shí)現(xiàn)類倔矾。
我們先來看看init()
方法妄均。
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
if (engine != null) {
engine.init();
}
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize mapper listener
mapperListener.init();
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
connector.init();
} catch (Exception e) {
String message = sm.getString(
"standardService.connector.initFailed", connector);
log.error(message, e);
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
throw new LifecycleException(message);
}
}
}
}
Service
的init()
操作主要做了幾件事情:
- Engine的初始化操作
- Service線程執(zhí)行器(tomcat中對(duì)
java.util.concurrent.Executor
的實(shí)現(xiàn))的初始化操作 - MapperListener的初始化操作
- 對(duì)Connectors(可能有多個(gè))的初始化操作
【注】:Engine、MapperListener哪自、Mapper和Connector我們會(huì)在后面章節(jié)詳細(xì)分析丰包,本節(jié)不做深入討論。我們這兒只需要知道Mapper的作用壤巷,主要是通過請(qǐng)求url快速和精確地找到相應(yīng)的Wrapper邑彪。
接下來我們看看Service
的start()
方法
@Override
protected void startInternal() throws LifecycleException {
if(log.isInfoEnabled())
log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING);
// Start our defined Container first
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
mapperListener.start();
// Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
try {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
}
}
和Service
的init()
類似,分別調(diào)用了Engine胧华、Executors寄症、MapperListener和Connectors的start()
方法。
總結(jié)
本節(jié)矩动,我們重新詳細(xì)全面地拆分了Tomcat組件的整體架構(gòu)圖有巧,知道了組件的包含關(guān)系和數(shù)量映射關(guān)系。其關(guān)系如下:
- 1個(gè)Catalina包含1個(gè)Server
- 1個(gè)Server包含多個(gè)Service
- 每個(gè)Service對(duì)應(yīng)有下面的組件
- 1個(gè)Engine
- 多個(gè)Connector
- 一個(gè)MapperListener