深入理解Tomcat(七)Server和Service

前言

回顧【深入理解Tomcat(三)架構(gòu)及組件】察净,同時(shí)參考tomcat自帶的server.xml诚撵,我們發(fā)現(xiàn)在連接器(Connector)和容器(Container)外層主要是ServerService。基于由外而內(nèi)互亮、由淺入深的方式,本文先來分析ServerService余素。

這兒豹休,我們在【深入理解Tomcat(三)架構(gòu)及組件】之上增加一張更為完整的整體結(jié)構(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

  1. Catalina.load()里面調(diào)用了Server.init()方法。
  2. Catalina.start()里面調(diào)用了Server.start()方法捧颅。

所以我們從Serverinit()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注釋挚币。

  1. 一個(gè)Server節(jié)點(diǎn)代表整個(gè)Catalina servlet容器。它的屬性代表這個(gè)servlet容器的所有特征谭梗。一個(gè)Server可以包含1個(gè)或多個(gè)Service忘晤,以及頂級(jí)名字資源。
  2. 正常情況下激捏,該接口的實(shí)現(xiàn)類也必須實(shí)現(xiàn)Lifecycle接口设塔,因此當(dāng)start()stop()方法被調(diào)用的時(shí)候,所有定義在其下面每個(gè)Servicestart()stop()也必須相應(yīng)地得到調(diào)用。
  3. 實(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è)Serverinit()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);
            }
        }
    }
}

Serviceinit()操作主要做了幾件事情:

  1. Engine的初始化操作
  2. Service線程執(zhí)行器(tomcat中對(duì)java.util.concurrent.Executor的實(shí)現(xiàn))的初始化操作
  3. MapperListener的初始化操作
  4. 對(duì)Connectors(可能有多個(gè))的初始化操作

【注】:Engine、MapperListener哪自、Mapper和Connector我們會(huì)在后面章節(jié)詳細(xì)分析丰包,本節(jié)不做深入討論。我們這兒只需要知道Mapper的作用壤巷,主要是通過請(qǐng)求url快速和精確地找到相應(yīng)的Wrapper邑彪。

接下來我們看看Servicestart()方法

@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);
            }
        }
    }
}

Serviceinit()類似,分別調(diào)用了Engine胧华、Executors寄症、MapperListener和Connectors的start()方法。

總結(jié)

本節(jié)矩动,我們重新詳細(xì)全面地拆分了Tomcat組件的整體架構(gòu)圖有巧,知道了組件的包含關(guān)系和數(shù)量映射關(guān)系。其關(guān)系如下:

  1. 1個(gè)Catalina包含1個(gè)Server
  2. 1個(gè)Server包含多個(gè)Service
  3. 每個(gè)Service對(duì)應(yīng)有下面的組件
    1. 1個(gè)Engine
    2. 多個(gè)Connector
    3. 一個(gè)MapperListener
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末悲没,一起剝皮案震驚了整個(gè)濱河市篮迎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖甜橱,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逊笆,死亡現(xiàn)場離奇詭異,居然都是意外死亡岂傲,警方通過查閱死者的電腦和手機(jī)难裆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來镊掖,“玉大人乃戈,你說我怎么就攤上這事∧督” “怎么了偏化?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長镐侯。 經(jīng)常有香客問我,道長驶冒,這世上最難降的妖魔是什么苟翻? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮骗污,結(jié)果婚禮上崇猫,老公的妹妹穿的比我還像新娘。我一直安慰自己需忿,他們只是感情好诅炉,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著屋厘,像睡著了一般涕烧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上汗洒,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天议纯,我揣著相機(jī)與錄音,去河邊找鬼溢谤。 笑死瞻凤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的世杀。 我是一名探鬼主播阀参,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瞻坝!你這毒婦竟也來了蛛壳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎炕吸,沒想到半個(gè)月后伐憾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赫模,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年树肃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瀑罗。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡胸嘴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出斩祭,到底是詐尸還是另有隱情劣像,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布摧玫,位于F島的核電站耳奕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏诬像。R本人自食惡果不足惜屋群,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坏挠。 院中可真熱鬧芍躏,春花似錦、人聲如沸降狠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽榜配。三九已至否纬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芥牌,已是汗流浹背烦味。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留壁拉,地道東北人谬俄。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像弃理,于是被迫代替她去往敵國和親溃论。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345