Tomcat 大家一直都在用,也用了好多年了必搞,但是 Tomcat 究竟是啥必指,內(nèi)部是咋樣的,不知道~ 來(lái)恕洲,我從源碼角度,給大家揭開它的面紗~
1. Tomcat架構(gòu)
這個(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()方法扣溺,如圖:
點(diǎn)開 createWebServer()方法:
進(jìn)入TomcatServletWebServerFactory#getWebServer():
一步步來(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ō)話~