深入源碼了解 Tomcat 的構(gòu)造

Tomcat 大家一直都在用,也用了好多年了必搞,但是 Tomcat 究竟是啥必指,內(nèi)部是咋樣的,不知道~ 來(lái)恕洲,我從源碼角度,給大家揭開它的面紗~

1. Tomcat架構(gòu)

tomcat架構(gòu)圖.png

這個(gè)是tomcat的架構(gòu)圖,專治密集恐懼癥患者~~虛線的代表同一多個(gè)

  • Server:代表一個(gè)運(yùn)行的tomcat實(shí)例,包含一個(gè)或多個(gè)service子容器

  • Service: 代表tomcat中一組處理請(qǐng)求桌吃,提供服務(wù)的組件,包含多個(gè)Connector和一個(gè)Container

  • Connector: 在指定IP秀睛、端口、協(xié)議下監(jiān)聽客戶端請(qǐng)求畜号,創(chuàng)建Request 痹升、Response 給 Engine,從Engine獲得響應(yīng)給客戶端。Connector是通過(guò)ProtocolHandler 來(lái)處理請(qǐng)求的,ProtocolHandler包含三個(gè)部件:Endpoint、Processor原朝、Adapter

    • Endpoint: 處理底層socket 網(wǎng)絡(luò)連接茂蚓。Acceptor 監(jiān)聽請(qǐng)求负乡,Handler處理接收到的socket, AsyncTimeout 檢查異步Request的超時(shí)
    • Processor: 將Endpoint 接收到的Socket 封裝成Request
    • Adapter: 將Request 交給Container 處理
  • Container: 容器的父接口狸涌,用于封裝和管理 Servlet朝捆,采用責(zé)任鏈設(shè)計(jì)模式何陆,包含四個(gè)組件

    • Engine: 可運(yùn)行的servlet 引擎實(shí)例巩剖,一個(gè)服務(wù)中只有一個(gè)晦炊。主要功能是將請(qǐng)求委托給適當(dāng)虛擬主機(jī)處理。
    • Host: Engine的子容器薄疚,一個(gè)Host代表一個(gè)虛擬主機(jī)躏筏,一個(gè)虛擬主機(jī)可以部署一個(gè)或多個(gè)Web App碴卧,每個(gè)Web App對(duì)應(yīng)一個(gè)Context
    • Context: 代表Servlet 的Context荧飞,是Servlet的基本運(yùn)行環(huán)境耳幢,表示web應(yīng)用本身邢隧。它最重要功能是管理里面的servlet
    • Wrapper: 表示一個(gè)單獨(dú)的Servlet,是Context的子容器系宜,也是最底層的容器苹粟,沒有子容器了找默。管理 Servlet的裝載、初始化布朦、執(zhí)行和資源回收

2. Tomcat源碼

我們來(lái)追蹤 SpringBoot啟動(dòng)過(guò)程,看一下它是怎么創(chuàng)建Tomcat的图筹。

跟到 ServletWebServerApplicationContext#refresh()方法扣溺,如圖:

onRefresh.png

點(diǎn)開 createWebServer()方法:

createWebServer.png

進(jìn)入TomcatServletWebServerFactory#getWebServer():

getWebServer.png

一步步來(lái)看tomcat的構(gòu)建過(guò)程。

  • 設(shè)置運(yùn)行路徑

    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    
  • 添加 Connector

    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);
    tomcat.getService().addConnector(connector);
    

    先點(diǎn)getService()進(jìn)去看下:

    public Service getService() {
      return getServer().findServices()[0];
    }
    

    再點(diǎn)getServer()看看:

    public Server getServer() {
          if (server != null) {
             return server;
          }
          System.setProperty("catalina.useNaming", "false");
          server = new StandardServer();
          initBaseDir();
          ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(basedir), null));
          server.setPort( -1 );
          // server里添加一個(gè)名為”tomcat“的service
          Service service = new StandardService();
          service.setName("Tomcat");
          server.addService(service);
          return server;
    }
    

    tomcat里兼贡,先創(chuàng)建server凿蒜,server設(shè)置關(guān)聯(lián)的service漂洋,service再添加 connector,跟我們上面的架構(gòu)圖一模一樣陈症。

    再來(lái)看Host:

    tomcat.getHost().setAutoDeploy(false);
    

    點(diǎn)擊getHost()進(jìn)去:

    public Host getHost() {
        Engine engine = getEngine();
        if (engine.findChildren().length > 0) {
            return (Host) engine.findChildren()[0];
         }
         Host host = new StandardHost();
         host.setName(hostname);
         // Engine添加host
         getEngine().addChild(host);
         return host;
    }
    

    可以看到,Host是被當(dāng)做子容器添加到Engine里的蚤蔓,對(duì)比架構(gòu)圖卦溢,沒騙你吧~~

    再點(diǎn)getEngine()進(jìn)去:

    public Engine getEngine() {
        Service service = getServer().findServices()[0];
        if (service.getContainer() != null) {
             return service.getContainer();
         }
         Engine engine = new StandardEngine();
         engine.setName( "Tomcat" );
         engine.setDefaultHost(hostname);
         // engine設(shè)置為service的容器
         service.setContainer(engine);
         return engine;
    }
    

    又可以看到,engine被當(dāng)做service的容器設(shè)置進(jìn)去了秀又,沒有問(wèn)題单寂。

    回到 getWebServer(),看這一行:

    configureEngine(tomcat.getEngine());
    

    點(diǎn)擊這個(gè)方法進(jìn)去:

    private void configureEngine(Engine engine) {     engine.setBackgroundProcessorDelay(this.backgroundProcessorDelay);
      for (Valve valve : this.engineValves) {
          engine.getPipeline().addValve(valve);
      }
    }
    

    engine里有pipeline吐辙,pipeline一個(gè)個(gè)添加valve宣决,串成鏈。

    好像還漏了架構(gòu)圖的兩個(gè)東西昏苏,context和servlet尊沸,回到getWebServer() 繼續(xù)點(diǎn)擊:

    prepareContext(tomcat.getHost(), initializers);
    

    點(diǎn)擊該方法進(jìn)去,看關(guān)鍵代碼:

    protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
      File documentRoot = getValidDocumentRoot();
      TomcatEmbeddedContext context = new TomcatEmbeddedContext();
          context.setName(getContextPath());
          ……
          if (isRegisterDefaultServlet()) {
              addDefaultServlet(context);
          }
          if (shouldRegisterJspServlet()) {
              addJspServlet(context);
              addJasperInitializer(context);
          }
          context.addLifecycleListener(new StaticResourceConfigurer(context));
          ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
          host.addChild(context);
          configureContext(context, initializersToUse);
          postProcessContext(context);
      }
    

    這里面都是設(shè)置context的贤惯,包括添加默認(rèn)的servlet洼专,最后context以子容器的形式添加到了host中。中孵构,看圖說(shuō)話~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末屁商,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子颈墅,更是在濱河造成了極大的恐慌棒假,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件精盅,死亡現(xiàn)場(chǎng)離奇詭異帽哑,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)叹俏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門妻枕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事屡谐∈雒矗” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵愕掏,是天一觀的道長(zhǎng)度秘。 經(jīng)常有香客問(wèn)我,道長(zhǎng)饵撑,這世上最難降的妖魔是什么剑梳? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮滑潘,結(jié)果婚禮上垢乙,老公的妹妹穿的比我還像新娘。我一直安慰自己语卤,他們只是感情好追逮,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著粹舵,像睡著了一般钮孵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上眼滤,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天巴席,我揣著相機(jī)與錄音,去河邊找鬼柠偶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛睬关,可吹牛的內(nèi)容都是我干的诱担。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼电爹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蔫仙!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起丐箩,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤摇邦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后屎勘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體施籍,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年概漱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了丑慎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖竿裂,靈堂內(nèi)的尸體忽然破棺而出玉吁,到底是詐尸還是另有隱情,我是刑警寧澤腻异,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布进副,位于F島的核電站,受9級(jí)特大地震影響悔常,放射性物質(zhì)發(fā)生泄漏影斑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一这嚣、第九天 我趴在偏房一處隱蔽的房頂上張望鸥昏。 院中可真熱鬧,春花似錦姐帚、人聲如沸吏垮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)膳汪。三九已至,卻和暖如春九秀,著一層夾襖步出監(jiān)牢的瞬間遗嗽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工鼓蜒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留痹换,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓都弹,卻偏偏與公主長(zhǎng)得像娇豫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子畅厢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349