從Servlet到Tomcat再到Jetty


我們直到Servlet是處理每個HTTP請求的最小單元,而這些Servlet又被web容器所管理,不同的web容器有不同的特性和應用特點,比如常見的web 容器Tomcat和Jetty。

(另外我們還知道一些高性能web框架比如netty,和tomcat的區別是什么呢?

  • tomcat是基於HTTP協議的web容器,常用來做前端請求的服務器,也支持NIO模式;
  • 而netty是一個網絡框架,可以基於HTTP/TCP/UTP,也可以自定義協議,自主開發不同的web服務器,以滿足不同的場景需求;)

 

本文旨在探究web容器組織管理servlet的機制,和web容器怎么去處理不同的線程請求。

 

 

 

從Tomcat啟動流程說起

Tomcat的系統結構:

 

 

 

 

 

 

Tomcat是一系列層級容器組成的web容器,其中最頂層的容器是Server,代表着整個服務器,從上圖中可以看出,一個Server可以包含至少一個Service,用於具體提供服務;

 

Service容器包含的來個主要組件是ConnetctorContainer;這是Tomcat最重要的兩個組件:

 

  1. Connector用於處理鏈接相關的事情,並提供Socket與Request和Response相關的轉化;
  2. Container用於封裝和管理Servlet,以及具體處理Request請求;

 

這種設計模式是網絡I/O常見的設計,為了能夠並發處理大量連接,一般會采取一個線程方式監聽客戶端請求;另一個線程采用NIO的形式select已經接收到數據的channel信道,處理請求,這樣的形式;

Tomcat7提供了三種Connector:Java Blocking Connector,Java Nio Blocking Connector,APR/native Connector。

  • Java Blocking Connector是Tomcat7默認的網絡連接處理方式,其處理模型為一個或多個線程(Acceptor)接收TCP連接,每個連接的請求都會交由單獨的線程(Worker)處理。
  • Java Nio Blocking Connector是Tomcat8默認的處理方式。為了避免連接過多(尤其是keepalive的連接會阻塞worker線程)造成的線程資源浪費,它在接收網絡連接的線程(Acceptor)和請求處理線程(Worker)之間增加了Poller事件隊列,每當有IO就緒事件都會放入Poller隊列,Poller線程會不斷消費隊列里的事件,交由Worker線程處理。
  • APR/native Connector,APR即Apache Portable Runtime Libraries,使用了native代碼來優化網絡請求的處理。

 

在容器等級中的最底層就是Context容器,負責直接管理Servlet,一個Context對應一個Web工程;

 

 Tomcat的啟動邏輯是基於觀察者設計的,先來看看啟動的時序圖:

 

 

 

容器的配置屬性由web.xml制定;

 

 

Tomcat處理一次http請求的時序圖:

 

 

 

 

有了一個整體的了解后,讓我們再來看看Tomcat是如何分發請求,以及處理多用戶同時請求和管理多級容器的;

 

 

Tomcat是如何分發請求

 

 Tomcat中使用Connector處理多線程的連接請求:

 先看看Connector的主要類圖:其中處理Socket的是HttpProcessor

 

 容器的總體設計: Container是容器的父接口,所有子容器都必須實現這個接口,其中用到的設計模式是責任鏈模式

Engine -> Host -> Context -> Wrapper 這四個容器是個分別包含的父子關系(參見上幾個圖)

四個容器的關系類圖:

 

 

來看看Jetty 

 

Jetty相較於Tomcat更加輕便,雖然架構更加簡單,但是看起來可並不輕松。Spring是設計初衷是用來管理應用中的實例Bean,因而是基於Bean的架構;Jetty則更傾向於流程和組件的管理,采用了基於handler的架構。handler的嵌套和鏈式結構,LifeCycle和doStart、doHandler模式無不印證了這點

 

Jetty基礎架構: 

 

 

 

 

 Jetty與Tomcat的區別

 

Jetty 的架構從前面的分析可知,它的所有組件都是基於 Handler 來實現,當然它也支持 JMX。但是主要的功能擴展都可以用 Handler 來實現。可以說 Jetty 是面向 Handler 的架構,就像 Spring 是面向 Bean 的架構,iBATIS 是面向 statement 一樣,而 Tomcat 是以多級容器構建起來的,它們的架構設計必然都有一個“元神”,所有以這個“元神“構建的其它組件都是肉身。

從設計模板角度來看 Handler 的設計實際上就是一個責任鏈模式,接口類 HandlerCollection 可以幫助開發者構建一個鏈,而另一個接口類 ScopeHandler 可以幫助你控制這個鏈的訪問順序。另外一個用到的設計模板就是觀察者模式,用這個設計模式控制了整個 Jetty 的生命周期,只要繼承了 LifeCycle 接口,你的對象就可以交給 Jetty 來統一管理了。所以擴展 Jetty 非常簡單,也很容易讓人理解,整體架構上的簡單也帶來了無比的好處,Jetty 可以很容易被擴展和裁剪。

相比之下,Tomcat 要臃腫很多,Tomcat 的整體設計上很復雜,前面說了 Tomcat 的核心是它的容器的設計,從 Server 到 Service 再到 engine 等 container 容器。作為一個應用服務器這樣設計無口厚非,容器的分層設計也是為了更好的擴展,這是這種擴展的方式是將應用服務器的內部結構暴露給外部使用者,使得如果想擴展 Tomcat,開發人員必須要首先了解 Tomcat 的整體設計結構,然后才能知道如何按照它的規范來做擴展。這樣無形就增加了對 Tomcat 的學習成本。不僅僅是容器,實際上 Tomcat 也有基於責任鏈的設計方式,像串聯 Pipeline 的 Vavle 設計也是與 Jetty 的 Handler 類似的方式。要自己實現一個 Vavle 與寫一個 Handler 的難度不相上下。表面上看,Tomcat 的功能要比 Jetty 強大,因為 Tomcat 已經幫你做了很多工作了,而 Jetty 只告訴,你能怎么做,如何做,有你去實現。

單純比較 Tomcat 與 Jetty 的性能意義不是很大,只能說在某種使用場景下,它表現的各有差異。因為它們面向的使用場景不盡相同。從架構上來看 Tomcat 在處理少數非常繁忙的連接上更有優勢,也就是說連接的生命周期如果短的話,Tomcat 的總體性能更高。

而 Jetty 剛好相反,Jetty 可以同時處理大量連接而且可以長時間保持這些連接。例如像一些 web 聊天應用非常適合用 Jetty 做服務器,像淘寶的 web 旺旺就是用 Jetty 作為 Servlet 引擎。

另外由於 Jetty 的架構非常簡單,作為服務器它可以按需加載組件,這樣不需要的組件可以去掉,這樣無形可以減少服務器本身的內存開銷,處理一次請求也是可以減少產生的臨時對象,這樣性能也會提高。另外 Jetty 默認使用的是 NIO 技術在處理 I/O 請求上更占優勢,Tomcat 默認使用的是 BIO,在處理靜態資源時,Tomcat 的性能不如 Jetty。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM