Tomcat原始碼簡單分析
前言
Tomcat是目前應用比較多的servlet容器,有複雜的結構,想要了解它,就應該順著開發者設計之初的思路來,先了解整體的結構,對整體有了一定的掌控後,再逐個分析,瞭解感興趣的細節。
架構設計
簡單介紹各個模組:
-
Server:伺服器的意思,代表整個tomcat伺服器,一個tomcat只有一個Server;
-
Service:Server中的一個邏輯功能層, 一個Server可以包含多個Service;
-
Connector:稱作聯結器,是Service的核心元件之一,一個Service可以有多個Connector,主要是連線客戶端請求;
-
Container:Service的另一個核心元件,按照層級有Engine,Host,Context,Wrapper四種,一個Service只有一個Engine,其主要作用是執行業務邏輯;
server.xml配置檔案
<?xml version="1.0" encoding="UTF-8"?> <Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.startup.VersionLoggerListener"/> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on"/> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/> <GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml"/> </GlobalNamingResources> <Service name="Catalina"> <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>
Tomcat啟動流程
啟動Tomcat會是執行startup.bat或者startup.sh檔案,這兩個檔案最後都會呼叫,org.apache.catalina.startup包下面Bootstrap類的main方法。
main方法先例項化了一個Bootstrap例項,接著呼叫了init方法。init方法是生命週期方法,接著看init的具體實現。
init方法,先初始化了類載入器。initClassLoaders方法具體實現如下:
Tomcat執行的是start操作,呼叫完init方法後,會執行load方法。
load方法通過反射呼叫Catalina類的load方法。
load方法中比較重要的方法是createStartDigester(),createStartDigester方法主要的作用就是幫我們例項化了所有的服務元件包括server,service和connect。
初始化操作完成後,接下來會執行catalina例項的start方法。Tomcat會預設載入org.apache.catalina.core.StandardServer作為Server的例項類。
在Server的start的方法裡面會執行service的start方法。在createStartDigester()方法裡面,會預設載入org.apache.catalina.core.StandardService類。會接著呼叫Service的start方法。
service中會呼叫connector的start方法。至此Tomcat啟動完畢
Tomcat執行流程
假設來自客戶的請求為:http://localhost:8080/test/index.jsp.請求被髮送到本機埠8080,被在那裡偵聽的Coyote HTTP/1.1 Connector獲得,然後
-
Connector把該請求交給它所在的Service的Engine來處理,並等待Engine的迴應
-
Engine獲得請求localhost:8080/test/index.jsp,匹配它所有虛擬主機Host
-
Engine匹配到名為localhost的Host(即使匹配不到也把請求交給該Host處理,因為該Host被定義為該Engine的預設主機)
-
localhost Host獲得請求/test/index.jsp,匹配它所擁有的所有Context
-
Host匹配到路徑為/test的Context(如果匹配不到就把該請求交給路徑名為""的Context去處理)
-
path="/test"的Context獲得請求/index.jsp,在它的mapping table中尋找對應的servlet
-
Context匹配到URL PATTERN為*.jsp的servlet,對應於JspServlet類,構造HttpServletRequest物件和HttpServletResponse物件,作為引數呼叫JspServlet的doGet或doPost方法
-
Context把執行完了之後的HttpServletResponse物件返回給Host
-
Host把HttpServletResponse物件返回給Engine
-
Engine把HttpServletResponse物件返回給Connector
-
Connector把HttpServletResponse物件返回給客戶browser
Server
Server是Tomcat最頂層的容器,代表著整個伺服器,即一個Tomcat只有一個Server,Server中包含至少一個Service元件,用於提供具體服務。這個在配置檔案中也得到很好的體現(port=”8005” shutdown=”SHUTDOWN”是在8005埠監聽到”SHUTDOWN”命令,伺服器就會停止)。
Tomcat中其標準實現是:org.apache.catalina.core.StandardServer類,繼承LifecycleMBeanBase,,tomcat為所有的元件都提供了生命週期管理。
Service
在conf/server.xml檔案中,可以看到Service元件包含了Connector元件和Engine元件(前面有提過,Engine就是一種容器),即Service相當於Connector和Engine元件的包裝器,將一個或者多個Connector和一個Engine建立關聯關係。在預設的配置檔案中,定義了一個叫Catalina 的服務,它將HTTP/1.1和AJP/1.3這兩個Connector與一個名為Catalina 的Engine關聯起來。
一個Server可以包含多個Service(它們相互獨立,只是公用一個JVM及類庫),一個Service負責維護多個Connector和一個Container。其標準實現是StandardService。
Connector
Connector主要負責處理與客戶端的通訊,Connector的例項用於監聽埠,接受來自客戶端的請求並將請求轉交給Engine處理,同時將來自Engine的答覆返回給客戶端。
Tomcat原始碼中與connector相關的類位於org.apache.coyote包中,Connector分為以下幾類:
Http Connector, 基於HTTP協議,負責建立HTTP連線。它又分為BIO Http Connector與NIO Http Connector兩種,後者提供非阻塞IO與長連線Comet支援。預設情況下,Tomcat使用的就是這個Connector。
AJP Connector, 基於AJP協議,AJP是專門設計用來為tomcat與http伺服器之間通訊專門定製的協議,能提供較高的通訊速度和效率。如與Apache伺服器整合時,採用這個協議。
APR HTTP Connector, 用C實現,通過JNI呼叫的。主要提升對靜態資源(如HTML、圖片、CSS、JS等)的訪問效能。現在這個庫已獨立出來可用在任何專案中。Tomcat在配置APR之後效能非常強勁。
Connector使用ProtocolHandler來處理請求的,不同的ProtocolHandler代表不同的連線型別,比如:Http11Protocol使用的是普通Socket來連線的(tomcat9已經刪除了這個類,不再採用BIO的方式),Http11NioProtocol使用的是NioSocket來連線的。
其中ProtocolHandler由包含了三個部件:Endpoint、Processor、Adapter。
1. Endpoint用來處理底層Socket的網路連線,Processor用於將Endpoint接收到的Socket封裝成Request(這個Request和ServletRequest無關),Adapter充當介面卡,用於將Request轉換為ServletRequest交給Container進行具體的處理。
2. Endpoint由於是處理底層的Socket網路連線,因此Endpoint是用來實現TCP/IP協議的,而Processor用來實現HTTP協議的,Adapter將請求適配到Servlet容器進行具體的處理。
3. Endpoint的抽象實現AbstractEndpoint裡面定義的Acceptor和AsyncTimeout兩個內部類和一個Handler介面。Acceptor用於監聽請求,AsyncTimeout用於檢查非同步Request的超時,Handler用於處理接收到的Socket,在內部呼叫Processor進行處理。
下面我們以org.apache.coyote.http11.Http11AprProtocol為例說明Connector的工作流程。
①它將工作委託給AprEndpoint類。
public Http11AprProtocol() {
endpoint = new AprEndpoint(); // 主要工作由AprEndpoint來完成
cHandler = new Http11ConnectionHandler(this); // inner class
((AprEndpoint) endpoint).setHandler(cHandler);
②在AprEndpoint.Acceptor類中的run()方法會接收一個客戶端新的連線請求.
protected class Acceptor extends AbstractEndpoint.Acceptor {
private final Log log = LogFactory.getLog(AprEndpoint.Acceptor.class);
@Override
public void run() {
int errorDelay = 0;
// Loop until we receive a shutdown command
while (running) {
③在AprEndpoint類中,有一個內部介面Handler,該介面定義如下:
public interface Handler extends AbstractEndpoint.Handler {
public SocketState process(SocketWrapper<Long> socket,
SocketStatus status);
}
④在Http11AprProtocol類中實現了AprEndpoint中的Handler介面,
protected static class Http11ConnectionHandler
extends AbstractConnectionHandler<Long,Http11AprProcessor> implements Handler {
並呼叫Http11AprProcessor類(該類實現了ActionHook回撥介面)。
protected Http11AprProcessor createProcessor() {
Http11AprProcessor processor = new Http11AprProcessor(
Container
Tomcat提供了一個Container介面來抽象容器,並且細分了4種類型的容器,分別是Engine、Host、Context和Wrapper,對應不同的概念層次。
· Engine:表示整個Catalina的servlet引擎
· Host:表示一個擁有數個上下文的虛擬主機
· Context:表示一個Web應用,一個context包含一個或多個wrapper
· Wrapper:表示一個獨立的servlet