Tomcat是什麼?html
Tomcat是開源的 Java Web 應用服務器,實現了 Java EE 的部分技術規範,好比 Java Servlet、Java Server Page、JSTL、Java WebSocket。Java EE 是 Sun 公 司爲企業級應用推出的標準平臺,定義了一系列用於企業級開發的技術規範。除了上述的以外,還有 EJB、Java Mail、JPA、JTA、JMS 等,而這些都依賴具體容器的實現。
上圖對比了 Java EE 容器的實現狀況,Tomcat 和 Jetty 都只提供了 Java Web 容器必需的 Servlet 和 JSP 規範,開發者要想實現其餘的功能,須要本身依賴其餘開源實現。java
Glassfish 是由 sun 公司推出,Java EE 最新規範出來以後,首先會在 Glassfish 上進行實現,因此是研究 Java EE 最新技術的首選。程序員
最多見的狀況是使用 Tomcat 做爲 Java Web 服務器,使用 Spring 提供的開箱即用的強大功能,並依賴其餘開源庫來完成負責的業務功能實現。web
Servlet容器面試
Tomcat 組成shell
以下圖所示,主要有 Container 和 Connector 以及相關組件構成。數據庫
Container組成後端
生命週期管理瀏覽器
Tomcat 爲了方便管理組件和容器的生命週期,定義了從建立、啓動、到中止、銷燬共 12 種狀態tomcat
tomcat 生命週期管理了內部狀態變化的規則控制,組件和容器只需實現相應的生命週期方法,便可完成各生命週期內的操做(initInternal、startInternal、stopInternal、 destroyInternal);
好比執行初始化操做時,會判斷當前狀態是否 New,若是不是則拋出生命週期異常;是的話則設置當前狀態爲 Initializing,並執行 initInternal 方法,由子類實現,方法執行成功則設置當前狀態爲 Initialized,執行失敗則設置爲 Failed 狀態;
Tomcat 的生命週期管理引入了事件機制,在組件或容器的生命週期狀態發生變化時會通知事件監聽器,監聽器經過判斷事件的類型來進行相應的操做。事件監聽器的添加能夠在 server.xml 文件中進行配置;
Tomcat 各種容器的配置過程是經過添加 listener 的方式來進行的,從而達到配置邏輯與容器的解耦。如 EngineConfig、HostConfig、ContextConfig。
Tomcat 的啓動過程
啓動從 Tomcat 提供的 start.sh 腳本開始,shell 腳本會調用 Bootstrap 的 main 方法,實際調用了 Catalina 相應的 load、start 方法。
load 方法會經過 Digester 進行 config/server.xml 的解析,在解析的過程當中會根據 xml 中的關係 和配置信息來建立容器,並設置相關的屬性。
接着 Catalina 會調用 StandardServer 的 init 和 start 方法進行容器的初始化和啓動。
按照 xml 的配置關係,server 的子元素是 service,service 的子元素是頂層容器 Engine,每層容器都持有本身的子容器,而這些元素都實現了生命週期管理的各個方法,所以就很容易的完成整個容器的啓動、關閉等生命週期的管理。
StandardServer 完成 init 和 start 方法調用後,會一直監聽來自 8005 端口(可配置)
若是接收 到 shutdown 命令,則會退出循環監聽,執行後續的 stop 和 destroy 方法,完成 Tomcat 容器的關閉。
同時也會調用 JVM 的 Runtime.getRuntime()﴿.addShutdownHook 方法,在虛擬機意外退出的時候來關閉容器。
全部容器都是繼承自 ContainerBase,基類中封裝了容器中的重複工做,負責啓動容器相關的組件 Loader、Logger、Manager、Cluster、Pipeline,啓動子容器(線程池併發啓動子容器,經過線程池 submit 多個線程,調用後返回 Future 對象,線程內部啓動子容器,接着調用 Future 對象 的 get 方法來等待執行結果)。
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 監聽了 StandardHost 容器的事件,在 start 方法中解析上述配置文件:
注:
ContextConfig 解析 context.xml 順序:
ContextConfig 解析 web.xml 順序:
注:
Servlet 生命週期
Servlet 是用 Java 編寫的服務器端程序,其主要功能在於交互式地瀏覽和修改數據,生成動態 Web 內容。
load on startup
當值爲 0 或者大於 0 時,表示容器在應用啓動時就加載這個 servlet; 當是一個負數時或者沒有指定時,則指示容器在該 servlet 被選擇時才加載; 正數的值越小,啓動該 servlet 的優先級越高;
single thread model
每次訪問 servlet,新建 servlet 實體對象,但並不能保證線程安全,同時 tomcat 會限制 servlet 的實例數目
最佳實踐:不要使用該模型,servlet 中不要有全局變量
請求處理過程
Pipeline 與 Valve
Pipeline 能夠理解爲現實中的管道,Valve 爲管道中的閥門,Request 和 Response 對象在管道中 通過各個閥門的處理和控制。
每一個容器的管道中都有一個必不可少的 basic valve,其餘的都是可選的,basic valve 在管道中最 後調用,同時負責調用子容器的第一個 valve。
Valve 中主要的三個方法:setNext、getNext、invoke;valve 之間的關係是單向鏈式結構,自己 invoke 方法中會調用下一個 valve 的 invoke 方法。
各層容器對應的 basic valve 分別是 StandardEngineValve、StandardHostValve、 StandardContextValve、StandardWrapperValve。
JSP引擎
JSP 生命週期
JSP元素
代碼片斷:<%>
JSP聲明:<%! ...="">
JSP表達式:<%=>
JSP註釋:<%-->
JSP指令:<%@ directive="" attribute="「value」">
JSP行爲:
HTML元素:html/head/body/div/p/…
JSP隱式對象:request、response、out、session、application、config、pageContext、page、Exception
JSP 元素說明
Jsp 解析過程
Connector
Http:HTTP 是超文本傳輸協議,是客戶端瀏覽器或其餘程序與 Web 服務器之間的應用層通訊協 議
AJP:Apache JServ 協議(AJP)是一種二進制協議,專門代理從 Web 服務器到位於後端的應用 程序服務器的入站請求
阻塞 IO
非阻塞 IO
IO多路複用
阻塞與非阻塞的區別在於進行讀操做和寫操做的系統調用時,若是此時內核態沒有數據可讀或者沒有緩衝空間可寫時,是否阻塞。
IO多路複用的好處在於可同時監聽多個socket的可讀和可寫事件,這樣就能使得應用能夠同時監聽多個socket,釋放了應用線程資源。
Tomcat各種Connector對比
Connector的實現模式有三種,分別是BIO、NIO、APR,能夠在server.xml中指定。
Apache Portable Runtime是一個高度可移植的庫,它是Apache HTTP Server 2.x的核心。
APR具備許多用途,包括訪問高級IO功能(如sendfile,epoll和OpenSSL),操做系統級功能(隨機數生成,系統狀態等)和本地進程處理(共享內存,NT管道和Unix套接字)
表格中字段含義說明:
NIO處理相關類
Acceptor線程負責接收鏈接,調用accept方法阻塞接收創建的鏈接,並對socket進行封裝成PollerEvent,指定註冊的事件爲op_read,並放入到EventQueue隊列中,PollerEvent的run方法邏輯的是將Selector註冊到socket的指定事件;
Poller線程從EventQueue獲取PollerEvent,並執行PollerEvent的run方法,調用Selector的select方法,若是有可讀的Socket則建立Http11NioProcessor,放入到線程池中執行
CoyoteAdapter是Connector到Container的適配器,Http11NioProcessor調用其提供的service方法,內部建立Request和Response對象,並調用最頂層容器的Pipeline中的第一個Valve的invoke方法
Mapper主要處理http url 到servlet的映射規則的解析,對外提供map方法
NIO Connector主要參數
Comet
Comet是一種用於web的推送技術,能使服務器實時地將更新的信息傳送到客戶端,而無須客戶端發出請求
在WebSocket出來以前,若是不適用comet,只能經過瀏覽器端輪詢Server來模擬實現服務器端推送。
Comet支持servlet異步處理IO,當鏈接上數據可讀時觸發事件,並異步寫數據(阻塞)
Tomcat要實現Comet,只需繼承HttpServlet同時,實現CometProcessor接口
Note:
異步Servlet
傳統流程:
異步處理流程:
Servlet 線程將請求轉交給一個異步線程來執行業務處理,線程自己返回至容器,此時 Servlet 尚未生成響應數據,異步線程處理完業務之後,能夠直接生成響應數據(異步線程擁有 ServletRequest 和 ServletResponse 對象的引用)
爲何web應用中支持異步?
推出異步,主要是針對那些比較耗時的請求:好比一次緩慢的數據庫查詢,一次外部REST API調用, 或者是其餘一些I/O密集型操做。這種耗時的請求會很快的耗光Servlet容器的線程池,繼而影響可擴展性。
Note:從客戶端的角度來看,request仍然像任何其餘的HTTP的request-response交互同樣,只是耗費了更長的時間而已
異步事件監聽
Note :
onError/ onTimeout觸發後,會緊接着回調onComplete
onComplete 執行後,就不可再操做request和response
牛皮了,馬士兵老師全網首播阿里P8級技術、實現大型淘寶實戰落
面試美團被JVM慘虐?阿里P9架構師用500分鐘把JVM從入門講到實戰#合集
清華啓蒙架構師馬士兵針對應屆生到開發十年的Java程序員作職業把脈