前言
Tomcat作為Java開發者接觸過最重要的web容器,在啓動和處理請求過程中做了海量的事情,初級開發者很少關心,使用SpringMvc之類上層框架一帶而過,然而這些部分是Java和網絡集大成之作,筆者要帶着大家走一遍一次請求,加深tomcat的認知。最好先調試好Tomcat源碼
Tomcat基礎架構
BootStrap和Catalina
BootStrap
BootStrap就是Tomcat的main函數所在位置,在使用過程中執行腳本catlina.sh或者bat文件即可執行java命令並調用BootStrap的main函數實現tomcat的啓動
public static void main(String args[]) {
if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to
// prevent a range of class not found exceptions.
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
......
}
Catalina
BootStrap是服務器的入口, 會通過start、stop、stopServer、stopServer等反射調用Catalina的對應方法
public void stopServer() throws Exception {
Method method =
catalinaDaemon.getClass().getMethod("stopServer", (Class []) null);
method.invoke(catalinaDaemon, (Object []) null);
}
Tomcat的核心配置文件server.xml
先來看一下tomcat的容器結構
tomcat容器有多層,Server、service、Engine、Host、Context等,圖中有省略
比如一個名稱為mytomcat的web項目,對應到Container部分,Context名稱就是mytomcat
再看一下Tomcat的配置文件server.xml
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener SSLEngine="on"
className="org.apache.catalina.core.AprLifecycleListener" />
<Listener className="org.apache.catalina.core.JasperListener" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource auth="Container" description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase"
pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector URIEncoding="UTF-8" connectionTimeout="20000"
port="80" protocol="HTTP/1.1" redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine defaultHost="localhost" name="Catalina">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase" />
</Realm>
<Host appBase="webapps" autoDeploy="true" name="localhost"
unpackWARs="true">
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs" pattern="%h %l %u %t "%r" %s %b" prefix="localhost_access_log."
suffix=".txt" />
</Host>
</Engine>
</Service>
在啓動tomcat的時候,catalina的load方法,會讀取server.xml文件的內容,通過Digester組件把server.xml解析成Server對象,並且把Server變成Catalina的成員變量。
我們看一下catalina的load方法中digester.parse()方法執行後的容器代碼結構
這樣的話,Tomcat的容器的層級結構就建立起來了。
網絡處理部分
Connector
tomcat可以處理多種協議,統一使用Connector組件來實現,在tomcat中一個Service可以對應多個Connector
ProtocolHandler
connector中有個很重要的成員變量protocolHandler, 用來實現多種協議的解析,
比如http協議。基於tcp做集羣的AJP協議
Endpoint
AbstractEndpoint是Tcp的處理入口,並且持有Executor;Connector的executor和ProtocolHandler的線程池都是使用這個executor
當有網絡任務到來的時候,tomcat會使用這個線程池來處理socket事件
Acceptor 和 Poller
Acceptor是封裝新連接的任務,Endpoint使用單獨的線程維護 Poller 是java NIO中Selector的封裝,當新連接到來時喚醒Acceptor線程註冊pollerEvent事件來監聽連接
Poller的實現是java的Selector