jetty源碼架構分析

from: http://docs.huihoo.com/jetty/1.html

一、 總括

     你瞭解Jetty 嗎,就像我們所熟知的Tomcat一樣, Jetty是一個免費的開放源碼的100%純Java的Http服務器和Servlet容器。



     Jetty具備以下特點:

     快速高效

     。Jetty是最快的Servlet服務器之一

     。Jetty可以處理上千個併發連接
     小巧嵌入
     。Jetty的jar只有600多K
     。可動態嵌入到應用程序,適合開發web2.0等應用

     應用廣泛

     。開源項目有Geronimo , JBoss , JOnAS

     。商業項目有IBM Tivoli, Sonic MQ and Cisco SESM等

     可到Jetty網站 http://jetty.mortbay.org/jetty/ 查看最新信息

     本文將通過對Jetty最新穩定版 Jetty5.1.5RC2 源碼的研究,向讀者展示Jetty在設計方面使用的不同設計理念, 希望對廣大開發者在設計自己的系統時有所幫助。

     Jetty按照功能可以分爲四個主個主要的部分,HttpServer, HttpContext,HttpHandler,HttpListener,詳見如下類圖:

<圖 1-1>

 

二、HttpServer及配置

     對於初次接觸Jetty的人一定會對上圖感到迷惑,其實在Jetty中 HttpServer是一個服務器的核心控制類, 我們可以看到,其它的組件類都是由該類擴展開來,HttpServer的作用就是在一系列的監聽器類和處理器類之間搭起了一個橋樑,有效的控制着消息在系 統內的傳遞,如下圖:
<圖 1-2 >
     HttpServer職責是接受從HttpListener傳遞過來的request(請 求),HttpServer通過對request的Host(主機)或Path(路徑)進行匹配,然後分發給相應的HttpContext(可以理解爲一 個web application)。
     這裏舉個例子,假設我們現在要建立一個提供靜態頁面web服務,頁面內容在c:\root \下,可以通過如此配置HttpServer:
     HttpServer server = new HttpServer(); // 創建一個新的HttpServer
     SocketListener listener = new SocketListener(); // 創建一個新監聽器
     listener.setPort(8080);// 設置監聽端口爲8080
     server.addListener(listener);// 將監聽類註冊到server中
     HttpContext context = new HttpContext(); // 創建一個新HttpContext
     context.setContextPath("/app/*"); // 設置訪問路徑
     context.setResourceBase("c:/root/"); // 設置靜態資源路徑
     context.addHandler(new ResourceHandler()); // 爲這個HttpContext添加一個靜態資源處理器
     server.addContext(context); // 將這個HttpContext註冊到server中
     server.start();// 最後啓動這個server
     當我們要建立一個提供動態頁面web服務時, 假設我們自己的 web 應用放在Jetty目錄下的webapps下並打好包文件名爲myapp.war, 可以通過如此配置HttpServer:
     Server server = new Server(); // 創建一個新的HttpServer
     SocketListener listener = new SocketListener();// 創建一個新監聽器
     listener.setPort(8080); // 設置監聽端口爲8080
     server.addListener(listener ); // 將監聽類註冊到server中
     server.addWebApplication("myapp","./webapps/myapp/"); // 將這個web應用註冊到這個Server中
     server.start(); // 最後啓動這個server
     短短數行代碼就可創建一個web服務器並啓動它,這有點類似於我們windows中的即插 即用的概念,需要什麼就添加什麼,把這些類以HttpServer爲核心組合在一起,就可以完成強大的功能。
 

三、Jetty Server

     1.上面我們探討了HttpServer的啓動,讀者一定還存在這樣疑問,整個Jetty 服務器是怎樣啓動的?
     首先我們可以在圖 1-1 看到左下角有一個Server類,這個類實際上繼承了HttpServer,當啓動Jetty服務器時,具體來說,在Jetty根目錄下命令行下如輸入 java -jar start.jar etc/demo.xml,注意這裏有一個配置文件 demo.xml做爲運行參數,這個參數也可以是其它的配置文件,也可是多個xml配置文件,其實這個配置文件好比我們使用struts時的struts -config.xml文件,將運行Server需要用到的組件寫在裏面,比如上一節中HttpServer的配置需要的組件類都可以寫在這個配置文件 中。
     2.我們自己部署到Jetty的webapps目錄下的web application,Jetty如何運行我們自己的web application?
     首先當我們按上述方法啓動Jetty Server時,就會調用Server類裏面的main方法,這個入口方法首先會構造一個Server類實例(其實也就構造了一個 HttpServer),創建實例過程中就會構造XmlConfiguration類的對象來讀取參數配置文件,之後再由這個配置文件產生的 XmlConfiguration對象來配置這個Server,配置過程其實是運用Java的反射機制調用Server的方法並傳入配置文件中所寫的參數 來向這個Server添加HttpListener,HttpContext,HttpHandler,web application(對應我們自己部署的web應用)。
     添加我們自己的web application過程中相應的就會讀取我們所熟知的/WEB-INF/web.xml來創建一個WebApplicationContext(這個 類繼承了HttpContext)的實例,同時也會創建WebApplicationContext自身的ServletHandler(實現了 HttpHandler接口),注意到ServletHandler中包含一組ServletHolder指向實際的Servlet,譬如說我們在 web.xml文件中配置了兩個Filter和一個Servlet,這裏就會有三個ServletHolder,實際處理請求時 ServeletHandler就會依次調用這三個ServletHolder傳入request,response處理(實際最後交給這兩個 Filter和Servlet處理),這樣我們自己做好的一個 web應用就掛載到這個Server上了,可以接受客戶端相應的request(請求)。
 

四、運行原理(請參考如下時序圖)

<圖 1-7 >

     上圖展示了一個request的處理過程,首先HttpListener監聽到客戶端發來 的請求創建一個HttpConnection實例(封裝了連接細節,比如從Socket連接中獲取的輸入流和輸出流), HttpConnection對象構建過程中會創建Jetty內部自定義的HttpRequest和HttpResponse對象,接着 HttpListener會調用這個HttpConnection實例的handle方法, HttpConnection實例就調用HttpRequest對象的read()方法讀取信息,調用HttpServer的service方法以 HttpRequest,HttpResponse爲參數傳給HttpServer,HttpServer又將HttpRequest和 HttpResponse分發給相應的HttpCotext,HttpContext最後將HttpRequest和HttpResponse交給自身的 HttpHandler 處理,在這裏HttpRequest,HttpResponse被再次封裝爲ServletHttpRequest和 ServletHttpResponse,其實這兩個類實現了我們所熟知的HttpServletRequest和 HttpServletResponse接口。

 

五、高級性能

     1.HttpHandler:

     該接口的實現類用於處理HttpContext分發過來的reqeust,不同的實現類的 有不同的處理功能,這裏介紹幾常用的HttpHandler實現類:
     ReourceHandler:用於處理靜態內容,如以擴展名爲.html的文件

     SecurityHandler:提供基本的安全驗證

     ForwardHandler:轉發一個request到另一個url

     ServletHandler:用於將request交由具體的Servlet類進行處理
     2.當你在看圖 1-2 時候會注意到HttpServer和HttpListener,HttpServer與HttpContext,HttpContext與 HttpHandler存在一對多的關係,下面就介紹一下它們之間的這種關係如何通過程序來配置.

     HttpListener & HttpServer:

     HttpListener是所有監聽器類的接口,如圖中的SocketListener (基於傳統的Socket技術)就實現了該接口,Jetty還有其它的實現該接口類,如SocketChannelListener(基於NIO技術)類 等,HttpListener職責主要是在服務器啓動後監聽相應端口的來自客戶端請求並建立連接(圖 1-1 中所示用HttpConnection封裝連接細節),監聽器可在同個IP上開啓多個端口爲同一個HttpServer 進行監聽,所以HttpListener和HttpServer是多對一的關係,如下圖:

<圖 1-3 >

     配置代碼:
     HttpServer server = new HttpServer();
     HttpListenrer listener1 = new SocketChanneListener();
     Listener1.setPort(8080);
     HttpListenrer listener1 = new SocketListener();
     Listener1.setPort(8443);
     server.addListener(listener1);
     server.addListener(listener2);
 

     HttpContext & HttpHandler:

     HttpContext相當於對應客戶端請求的URL或某個虛擬機, 其子類中包含若干個HttpHandler, 當接受到request(請求)時,HttpContext會依次(按某個預定的次序)把request交給這些HttpHandler處理,直到這個 request被標示處理過爲止, 需要注意的是這個request可能被多個HttpHandler處理,但只能有一個HttpHandler能標示這個request已被處理過.

     一個典型的HttpContext有用於安全處理、靜態資源處理及Servlet類的 HttpHandler,如下圖:
<圖 1-4>
     配置代碼:
     HttpContext context = new HttpContext();
     context.setContextPath(「/myapp/*」);
     HttpHandler securitHandler = new SecurityHandler();
     HttpHandler resourceHandler = new ResourceHandler();
     HttpHandler servletHandler = new ServletHandler();
     context.addHandler(securitHandler);
     context.addHandler(resourceHandler);
     context.addHandler(servletHandler);
 
     HttpServer & HttpContext:
     一般的HTTP服務器軟件可以同時處理多個web application,同樣一個HttpServer可以包含多個HttpContext,如下圖可以通過同一個端口的監聽類來映射多個 HttpContext:

<圖 1-5 >

     配置代碼:
     HttpServer server = new HttpServer();
     HttpContext context1 = new HttpContext();
     context1.setContextPath(「/app1/*」);
     HttpContext context2 = new HttpContext();
     context2.setContextPath(「/app2/*」);
     server.addContext(context1);
 
     HttpServer & HttpLister & HttpContext:

     另外Jetty對多網卡(多個IP地址,不同的主機名)的服務器也提供了很好的支持,每個 HttpContext都有自身的HttpServer:

<圖 1-6 >

     配置代碼:
     HttpServer server1 = new HttpServer();
     SocketListener listener1 = new SocketListener();

     listener1.setHost(「www.app1.com」);//orListener1.setHost(「www.app2.com」)

     listener2.setPort(80);

     HttpContext context1 = new HttpContext();

     context1.setContextPath(「/」);

     server1.addListener(listener1);

     server1.addContext(context1);

 
     3.Jetty對高併發的支持
<圖 1-8>
     如果多用戶請求服務就會涉及到多線程的管理,如圖 1-8,Jetty中主要由ThreadPool負責管理多線程,注意其中Pool.PondLife是Pool的一個內部接口, ThreadPool.PoolThread是ThreadPool的一個內部線程類,我們看到Pool.PondLife和Pool存在一個聚集的關 系,實際上Pool對象中存放在是一個個ThreadPool.PoolThread線程對象,當有新用戶連接上Server時,ThreadPool就 從Pool中取一個空閒的線程爲當前用戶連接服務。
 

六、小結

     本文通過圖示簡要介紹了Jetty整個體系架構和主要的組件類及服務器的啓動執行過程,其 實Jetty 通常被用來做爲內嵌的Web Server來使用,一些常見的服務器軟件,如Apache Cocoon、JBoss ,JOnAs等都會採用Jetty作爲Web解決方案;另外由於Jetty在性能及穩定性要優於同類HTTP Server的原因,Jetty已在國外已很流行,鑑於這一點,本文作者可以預測在不久的將來Jetty同樣也會在國內流行開來。