Tomcat是什么?
Tomcat是開(kāi)源的 Java Web 應用服務(wù)器,實(shí)現了 Java EE 的部分技術(shù)規范,比如 Java Servlet、Java Server Page、JSTL、Java WebSocket。Java EE 是 Sun 公 司為企業(yè)級應用推出的標準平臺,定義了一系列用于企業(yè)級開(kāi)發(fā)的技術(shù)規范。除了上述的之外,還有 EJB、Java Mail、JPA、JTA、JMS 等,而這些都依賴(lài)具體容器的實(shí)現。
上圖對比了 Java EE 容器的實(shí)現情況,Tomcat 和 Jetty 都只提供了 Java Web 容器必需的 Servlet 和 JSP 規范,開(kāi)發(fā)者要想實(shí)現其他的功能,需要自己依賴(lài)其他開(kāi)源實(shí)現。
Glassfish 是由 sun 公司推出,Java EE 最新規范出來(lái)之后,首先會(huì )在 Glassfish 上進(jìn)行實(shí)現,所以是研究 Java EE 最新技術(shù)的首選。
最常見(jiàn)的情況是使用 Tomcat 作為 Java Web 服務(wù)器,使用 Spring 提供的開(kāi)箱即用的強大功能,并依賴(lài)其他開(kāi)源庫來(lái)完成負責的業(yè)務(wù)功能實(shí)現。
Servlet容器
Tomcat 組成
如下圖所示,主要有 Container 和 Connector 以及相關(guān)組件構成。
Container組成
生命周期管理
Tomcat 為了方便管理組件和容器的生命周期,定義了從創(chuàng )建、啟動(dòng)、到停止、銷(xiāo)毀共 12 種狀態(tài)
tomcat 生命周期管理了內部狀態(tài)變化的規則控制,組件和容器只需實(shí)現相應的生命周期方法,即可完成各生命周期內的操作(initInternal、startInternal、stopInternal、 destroyInternal);
比如執行初始化操作時(shí),會(huì )判斷當前狀態(tài)是否 New,如果不是則拋出生命周期異常;是的話(huà)則設置當前狀態(tài)為 Initializing,并執行 initInternal 方法,由子類(lèi)實(shí)現,方法執行成功則設置當前狀態(tài)為 Initialized,執行失敗則設置為 Failed 狀態(tài);
Tomcat 的生命周期管理引入了事件機制,在組件或容器的生命周期狀態(tài)發(fā)生變化時(shí)會(huì )通知事件監聽(tīng)器,監聽(tīng)器通過(guò)判斷事件的類(lèi)型來(lái)進(jìn)行相應的操作。事件監聽(tīng)器的添加可以在 server.xml 文件中進(jìn)行配置;
Tomcat 各類(lèi)容器的配置過(guò)程是通過(guò)添加 listener 的方式來(lái)進(jìn)行的,從而達到配置邏輯與容器的解耦。如 EngineConfig、HostConfig、ContextConfig。
Tomcat 的啟動(dòng)過(guò)程

啟動(dòng)從 Tomcat 提供的 start.sh 腳本開(kāi)始,shell 腳本會(huì )調用 Bootstrap 的 main 方法,實(shí)際調用了 Catalina 相應的 load、start 方法。
load 方法會(huì )通過(guò) Digester 進(jìn)行 config/server.xml 的解析,在解析的過(guò)程中會(huì )根據 xml 中的關(guān)系 和配置信息來(lái)創(chuàng )建容器,并設置相關(guān)的屬性。
接著(zhù) Catalina 會(huì )調用 StandardServer 的 init 和 start 方法進(jìn)行容器的初始化和啟動(dòng)。
按照 xml 的配置關(guān)系,server 的子元素是 service,service 的子元素是頂層容器 Engine,每層容器都持有自己的子容器,而這些元素都實(shí)現了生命周期管理的各個(gè)方法,因此就很容易的完成整個(gè)容器的啟動(dòng)、關(guān)閉等生命周期的管理。
StandardServer 完成 init 和 start 方法調用后,會(huì )一直監聽(tīng)來(lái)自 8005 端口(可配置)
如果接收 到 shutdown 命令,則會(huì )退出循環(huán)監聽(tīng),執行后續的 stop 和 destroy 方法,完成 Tomcat 容器的關(guān)閉。
同時(shí)也會(huì )調用 JVM 的 Runtime.getRuntime()?.addShutdownHook 方法,在虛擬機意外退出的時(shí)候來(lái)關(guān)閉容器。
所有容器都是繼承自 ContainerBase,基類(lèi)中封裝了容器中的重復工作,負責啟動(dòng)容器相關(guān)的組件 Loader、Logger、Manager、Cluster、Pipeline,啟動(dòng)子容器(線(xiàn)程池并發(fā)啟動(dòng)子容器,通過(guò)線(xiàn)程池 submit 多個(gè)線(xiàn)程,調用后返回 Future 對象,線(xiàn)程內部啟動(dòng)子容器,接著(zhù)調用 Future 對象 的 get 方法來(lái)等待執行結果)。
List<future> results = new ArrayList<future>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
boolean fail = false;
for (Futureresult :results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString('containerBase.threadedStartFailed'), e);
fail = true;
}
}
Web 應用的部署方式
注:catalina.home:安裝目錄;catalina.base:工作目錄;默認值 user.dir
HostConfig 監聽(tīng)了 StandardHost 容器的事件,在 start 方法中解析上述配置文件:
注:
ContextConfig 解析 context.xml 順序:
ContextConfig 解析 web.xml 順序:
注:
Servlet 生命周期

Servlet 是用 Java 編寫(xiě)的服務(wù)器端程序,其主要功能在于交互式地瀏覽和修改數據,生成動(dòng)態(tài) Web 內容。
load on startup
當值為 0 或者大于 0 時(shí),表示容器在應用啟動(dòng)時(shí)就加載這個(gè) servlet; 當是一個(gè)負數時(shí)或者沒(méi)有指定時(shí),則指示容器在該 servlet 被選擇時(shí)才加載; 正數的值越小,啟動(dòng)該 servlet 的優(yōu)先級越高;
single thread model
每次訪(fǎng)問(wèn) servlet,新建 servlet 實(shí)體對象,但并不能保證線(xiàn)程安全,同時(shí) tomcat 會(huì )限制 servlet 的實(shí)例數目
最佳實(shí)踐:不要使用該模型,servlet 中不要有全局變量
請求處理過(guò)程

Pipeline 與 Valve

Pipeline 可以理解為現實(shí)中的管道,Valve 為管道中的閥門(mén),Request 和 Response 對象在管道中 經(jīng)過(guò)各個(gè)閥門(mén)的處理和控制。
每個(gè)容器的管道中都有一個(gè)必不可少的 basic valve,其他的都是可選的,basic valve 在管道中最 后調用,同時(shí)負責調用子容器的第一個(gè) valve。
Valve 中主要的三個(gè)方法:setNext、getNext、invoke;valve 之間的關(guān)系是單向鏈式結構,本身 invoke 方法中會(huì )調用下一個(gè) valve 的 invoke 方法。
各層容器對應的 basic valve 分別是 StandardEngineValve、StandardHostValve、 StandardContextValve、StandardWrapperValve。
JSP引擎

JSP 生命周期
JSP元素
代碼片段:<%>
JSP聲明:<%! ...=''>
JSP表達式:<%=>
JSP注釋?zhuān)?lt;%-->
JSP指令:<%@ directive='' attribute='“value”'>
JSP行為:
HTML元素:html/head/body/div/p/…
JSP隱式對象:request、response、out、session、application、config、pageContext、page、Exception
JSP 元素說(shuō)明
Jsp 解析過(guò)程

Connector

Http:HTTP 是超文本傳輸協(xié)議,是客戶(hù)端瀏覽器或其他程序與 Web 服務(wù)器之間的應用層通信協(xié) 議
AJP:Apache JServ 協(xié)議(AJP)是一種二進(jìn)制協(xié)議,專(zhuān)門(mén)代理從 Web 服務(wù)器到位于后端的應用 程序服務(wù)器的入站請求
阻塞 IO

非阻塞 IO

IO多路復用

阻塞與非阻塞的區別在于進(jìn)行讀操作和寫(xiě)操作的系統調用時(shí),如果此時(shí)內核態(tài)沒(méi)有數據可讀或者沒(méi)有緩沖空間可寫(xiě)時(shí),是否阻塞。
IO多路復用的好處在于可同時(shí)監聽(tīng)多個(gè)socket的可讀和可寫(xiě)事件,這樣就能使得應用可以同時(shí)監聽(tīng)多個(gè)socket,釋放了應用線(xiàn)程資源。
Tomcat各類(lèi)Connector對比

Connector的實(shí)現模式有三種,分別是BIO、NIO、APR,可以在server.xml中指定。
Apache Portable Runtime是一個(gè)高度可移植的庫,它是Apache HTTP Server 2.x的核心。
APR具有許多用途,包括訪(fǎng)問(wèn)高級IO功能(如sendfile,epoll和OpenSSL),操作系統級功能(隨機數生成,系統狀態(tài)等)和本地進(jìn)程處理(共享內存,NT管道和Unix套接字)
表格中字段含義說(shuō)明:
NIO處理相關(guān)類(lèi)

Acceptor線(xiàn)程負責接收連接,調用accept方法阻塞接收建立的連接,并對socket進(jìn)行封裝成PollerEvent,指定注冊的事件為op_read,并放入到EventQueue隊列中,PollerEvent的run方法邏輯的是將Selector注冊到socket的指定事件;
Poller線(xiàn)程從EventQueue獲取PollerEvent,并執行PollerEvent的run方法,調用Selector的select方法,如果有可讀的Socket則創(chuàng )建Http11NioProcessor,放入到線(xiàn)程池中執行
CoyoteAdapter是Connector到Container的適配器,Http11NioProcessor調用其提供的service方法,內部創(chuàng )建Request和Response對象,并調用最頂層容器的Pipeline中的第一個(gè)Valve的invoke方法
Mapper主要處理http url 到servlet的映射規則的解析,對外提供map方法
NIO Connector主要參數

Comet
Comet是一種用于web的推送技術(shù),能使服務(wù)器實(shí)時(shí)地將更新的信息傳送到客戶(hù)端,而無(wú)須客戶(hù)端發(fā)出請求
在WebSocket出來(lái)之前,如果不適用comet,只能通過(guò)瀏覽器端輪詢(xún)Server來(lái)模擬實(shí)現服務(wù)器端推送。
Comet支持servlet異步處理IO,當連接上數據可讀時(shí)觸發(fā)事件,并異步寫(xiě)數據(阻塞)

Tomcat要實(shí)現Comet,只需繼承HttpServlet同時(shí),實(shí)現CometProcessor接口
Note:
異步Servlet

傳統流程:

異步處理流程:
Servlet 線(xiàn)程將請求轉交給一個(gè)異步線(xiàn)程來(lái)執行業(yè)務(wù)處理,線(xiàn)程本身返回至容器,此時(shí) Servlet 還沒(méi)有生成響應數據,異步線(xiàn)程處理完業(yè)務(wù)以后,可以直接生成響應數據(異步線(xiàn)程擁有 ServletRequest 和 ServletResponse 對象的引用)
為什么web應用中支持異步?
推出異步,主要是針對那些比較耗時(shí)的請求:比如一次緩慢的數據庫查詢(xún),一次外部REST API調用, 或者是其他一些I/O密集型操作。這種耗時(shí)的請求會(huì )很快的耗光Servlet容器的線(xiàn)程池,繼而影響可擴展性。
Note:從客戶(hù)端的角度來(lái)看,request仍然像任何其他的HTTP的request-response交互一樣,只是耗費了更長(cháng)的時(shí)間而已
異步事件監聽(tīng)
Note :
onError/ onTimeout觸發(fā)后,會(huì )緊接著(zhù)回調onComplete
onComplete 執行后,就不可再操作request和response
聯(lián)系客服