Tomcat8原始碼分析系列-tomcat框架設計
總體架構
如上圖所示,tomcat由Server、Service、Engine、Connerctor、Host、Context元件組成,其中帶有s的代表在一個tomcat例項上可以存在多個元件,比如Context(s),tomcat允許我們部署多個應用,每個應用對應一個Context。這些元件在tomcat的conf/server.xml檔案中可以找到,對tomcat的調優需要改動該檔案
server.xml
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout ="20000"
redirectPort="8443" />
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
Server
Server元件對應org.apache.catalina.Server介面,類圖如上所示。
- Server繼承至LifeCycle,LifeCycle是一個非常重要的介面,各大元件都繼承了這個介面,用於管理tomcat的生命週期,比如init、start、stop、destory;另外,它使用了觀察者模式,LifeCycle是一個監聽者,它會向註冊的LifecycleListener觀察者發出各種事件
- Server提供了findService、getCatalina、getCatalinaHome、getCatalinaBase等介面,支援查詢、遍歷Service元件,這裡似乎看到了和Serivce元件的些許聯絡
public interface Server extends Lifecycle {
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 void await();
public Service findService(String name);
public Service[] findServices();
public void removeService(Service service);
public Object getNamingToken();
}
Service
Service的預設實現類是StardardService,類結構和StardardServer很相似,也是繼承至LifecycleMBeanBase,實現了Service介面
由Service介面不難發現Service元件的內部結構
- 持有Engine例項
- 持有Server例項
- 可以管理多個Connector例項
- 持有Executor引用
public class StandardService extends LifecycleMBeanBase implements Service {
// 省略若干程式碼
}
public interface Service extends Lifecycle {
public Engine getContainer();
public void setContainer(Engine engine);
public String getName();
public void setName(String name);
public Server getServer();
public void setServer(Server server);
public ClassLoader getParentClassLoader();
public void setParentClassLoader(ClassLoader parent);
public String getDomain();
public void addConnector(Connector connector);
public Connector[] findConnectors();
public void removeConnector(Connector connector);
public void addExecutor(Executor ex);
public Executor[] findExecutors();
public Executor getExecutor(String name);
public void removeExecutor(Executor ex);
Mapper getMapper();
}
Connector
Connector是tomcat中監聽TCP埠的元件,server.xml預設定義了兩個Connector,分別用於監聽http、ajp埠。對應的程式碼是org.apache.catalina.connector.Connector,它是一個實現類,並且實現了Lifecycle介面
http
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
http對應的Connector配置如上所示,其中protocol用於指定http協議的版本,還可以支援2.0;connectionTimeout定義了連線超時時間,單位是毫秒;redirectPort是SSL的重定向埠,它會把請求重定向到8443這個埠
AJP
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
Apache jserver協議(AJP)是一種二進位制協議,它可以將來自web伺服器的入站請求傳送到位於web伺服器後的應用伺服器。如果我們希望把Tomcat整合到現有的(或新的)Apache http server中,並且希望Apache能夠處理web應用程式中包含的靜態內容,或者使用Apache的SSL處理,我們便可以使用該協議。但是,在實際的專案應用中,AJP協議並不常用,大多數應用場景會使用nginx+tomcat實現負載。
Container
org.apache.catalina.Container介面定義了容器的api,它是一個處理使用者servlet請求並返回物件給web使用者的模組,它有四種不同的容器:
- Engine,表示整個Catalina的servlet引擎
- Host,表示一個擁有若干個Context的虛擬主機
- Context,表示一個Web應用,一個context包含一個或多個wrapper
- Wrapper,表示一個獨立的servlet
Engine、Host、Context、Wrapper都有一個預設的實現類StandardXXX,均繼承至ContainerBase。此外,一個容器還包含一系列的Lodder、Logger、Manager、Realm和Resources等
一個容器可以有一個或多個低層次上的子容器,並且一個Catalina功能部署並不一定需要全部四種容器。一個Context有一個或多個wrapper,而wrapper作為容器層次中的最底層,不能包含子容器。從一個容器新增到另一容器中可以使用在Container介面中定義的addChild()方法義:
public void addChild(Container child);
刪除一個容器可以使用Container介面中定義的removeChild()方法:
public void removeChild(Container child);
另外容器介面支援子介面查詢和獲得所有子介面集合的方法findChild和findChildren方法:
public Container findChild(String name);
public Container[] findChildren();
Engine
Engine表示Catalina的Servlet引擎,如果使用了Engine的話,則它是Catalina的頂層容器,因此在StardardCataline的setParent()方法中直接丟擲的異常
public interface Engine extends Container {
public String getDefaultHost();
public void setDefaultHost(String defaultHost);
public String getJvmRoute();
public void setJvmRoute(String jvmRouteId);
public Service getService();
public void setService(Service service);
}
public class StandardEngine extends ContainerBase implements Engine {
// other code...
public void setParent(Container container) {
throw new IllegalArgumentException(sm.getString("standardEngine.notParent"));
}
}
server.xml
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
Host
Host定義了一個虛擬主機,正所謂虛擬主機,當然是可以用來部署應用程式的,Tomcat的Host也是如此。它在server.xml中定義了一個localhost的Host,應用根目錄在webapps下面,預設是支援解壓重新部署的。
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">...</Host>
Context
Context代表一個獨立的web應用,針對每個Context,tomcat都是使用不同的Classloader避免類衝突。如果我們希望使用一個自定義的目錄作為部署路徑的話,可以在server.xml中新增Context即可
<Context path="/static" docBase="D:/static" reloadable="true"></Context>
程式碼模組簡介
catalina包
Tomcat的核心模組,包括了HttpServlet、HttpSession的實現,以及各大元件的實現,這塊的程式碼量是最多的,也是最複雜的一部分
coyote包
這塊主要用於支援各種協議,比如http1.1、http2.0、ajp等,程式碼量較少
tomcat包
tomcat的基礎包,包括了資料庫連線池、websocket實現、tomcate的jni、工具類。org.apache.tomcat.util包的程式碼量也不少,其中還包括了對jdk原始碼的擴充套件,比如執行緒池。
下圖羅列各個模組的大致用途以及程式碼量