Jetty 源碼分析
一、 總括 你了解Jetty 嗎,就像我們所熟知的Tomcat一樣, Jetty是一個免費(fèi)的開放源碼的100%純Java的Http服務(wù)器和Servlet容器。
|
|
Jetty具備以下特點(diǎn): |
|
快速高效
|
|
。Jetty是最快的Servlet服務(wù)器之一 |
|
。Jetty可以處理上千個并發(fā)連接 | |
小巧嵌入 | |
。Jetty的jar只有600多K | |
。可動態(tài)嵌入到應(yīng)用程序,適合開發(fā)web2.0等應(yīng)用 | |
應(yīng)用廣泛 |
|
。商業(yè)項目有IBM Tivoli, Sonic MQ and Cisco SESM等 |
|
可到Jetty網(wǎng)站 http://jetty.mortbay.org/jetty/ 查看最新信息 |
|
本文將通過對Jetty最新穩(wěn)定版 Jetty5.1.5RC2 源碼的研究,向讀者展示Jetty在設(shè)計方面使用的不同設(shè)計理念, 希望對廣大開發(fā)者在設(shè)計自己的系統(tǒng)時有所幫助。 | |
Jetty按照功能可以分為四個主個主要的部分,HttpServer, HttpContext,HttpHandler,HttpListener,詳見如下類圖: |
|
![]() |
|
<圖 1-1> |
|
|
|
二、HttpServer及配置 |
|
對于初次接觸Jetty的人一定會對上圖感到迷惑,其實(shí)在Jetty中 HttpServer是一個服務(wù)器的核心控制類, 我們可以看到,其它的組件類都是由該類擴(kuò)展開來,HttpServer的作用就是在一系列的監(jiān)聽器類和處理器類之間搭起了一個橋梁,有效的控制著消息在系統(tǒng)內(nèi)的傳遞,如下圖: | |
![]() |
|
<圖 1-2 >
|
|
HttpServer職責(zé)是接受從HttpListener傳遞過來的request(請求),HttpServer通過對request的Host(主機(jī))或Path(路徑)進(jìn)行匹配,然后分發(fā)給相應(yīng)的HttpContext(可以理解為一個web application)。 | |
這里舉個例子,假設(shè)我們現(xiàn)在要建立一個提供靜態(tài)頁面web服務(wù),頁面內(nèi)容在c:\root \下,可以通過如此配置HttpServer: | |
HttpServer server = new HttpServer(); // 創(chuàng)建一個新的HttpServer | |
SocketListener listener = new SocketListener(); // 創(chuàng)建一個新監(jiān)聽器 | |
listener.setPort(8080);// 設(shè)置監(jiān)聽端口為8080 | |
server.addListener(listener);// 將監(jiān)聽類注冊到server中 | |
HttpContext context = new HttpContext(); // 創(chuàng)建一個新HttpContext | |
context.setContextPath("/app/*"); // 設(shè)置訪問路徑 | |
context.setResourceBase("c:/root/"); // 設(shè)置靜態(tài)資源路徑 | |
context.addHandler(new ResourceHandler()); // 為這個HttpContext添加一個靜態(tài)資源處理器 | |
server.addContext(context); // 將這個HttpContext注冊到server中 | |
server.start();// 最后啟動這個server | |
當(dāng)我們要建立一個提供動態(tài)頁面web服務(wù)時, 假設(shè)我們自己的 web 應(yīng)用放在Jetty目錄下的webapps下并打好包文件名為myapp.war, 可以通過如此配置HttpServer: | |
Server server = new Server(); // 創(chuàng)建一個新的HttpServer | |
SocketListener listener = new SocketListener();// 創(chuàng)建一個新監(jiān)聽器 | |
listener.setPort(8080); // 設(shè)置監(jiān)聽端口為8080 | |
server.addListener(listener ); // 將監(jiān)聽類注冊到server中 | |
server.addWebApplication("myapp","./webapps/myapp/"); // 將這個web應(yīng)用注冊到這個Server中 | |
server.start(); // 最后啟動這個server | |
短短數(shù)行代碼就可創(chuàng)建一個web服務(wù)器并啟動它,這有點(diǎn)類似于我們windows中的即插即用的概念,需要什么就添加什么,把這些類以HttpServer為核心組合在一起,就可以完成強(qiáng)大的功能。 | |
三、Jetty Server |
|
1.上面我們探討了HttpServer的啟動,讀者一定還存在這樣疑問,整個Jetty 服務(wù)器是怎樣啟動的? | |
首先我們可以在圖 1-1 看到左下角有一個Server類,這個類實(shí)際上繼承了HttpServer,當(dāng)啟動Jetty服務(wù)器時,具體來說,在Jetty根目錄下命令行下如輸入 JAVA -jar start.jar etc/demo.xml,注意這里有一個配置文件 demo.xml做為運(yùn)行參數(shù),這個參數(shù)也可以是其它的配置文件,也可是多個xml配置文件,其實(shí)這個配置文件好比我們使用struts時的struts -config.xml文件,將運(yùn)行Server需要用到的組件寫在里面,比如上一節(jié)中HttpServer的配置需要的組件類都可以寫在這個配置文件中。 | |
2.我們自己部署到Jetty的webapps目錄下的web application,Jetty如何運(yùn)行我們自己的web application? | |
首先當(dāng)我們按上述方法啟動Jetty Server時,就會調(diào)用Server類里面的main方法,這個入口方法首先會構(gòu)造一個Server類實(shí)例(其實(shí)也就構(gòu)造了一個 HttpServer),創(chuàng)建實(shí)例過程中就會構(gòu)造XmlConfiguration類的對象來讀取參數(shù)配置文件,之后再由這個配置文件產(chǎn)生的 XmlConfiguration對象來配置這個Server,配置過程其實(shí)是運(yùn)用Java的反射機(jī)制調(diào)用Server的方法并傳入配置文件中所寫的參數(shù)來向這個Server添加HttpListener,HttpContext,HttpHandler,web application(對應(yīng)我們自己部署的web應(yīng)用)。 | |
添加我們自己的web application過程中相應(yīng)的就會讀取我們所熟知的/WEB-INF/web.xml來創(chuàng)建一個WebApplicationContext(這個類繼承了HttpContext)的實(shí)例,同時也會創(chuàng)建WebApplicationContext自身的ServletHandler(實(shí)現(xiàn)了 HttpHandler接口),注意到ServletHandler中包含一組ServletHolder指向?qū)嶋H的Servlet,譬如說我們在 web.xml文件中配置了兩個Filter和一個Servlet,這里就會有三個ServletHolder,實(shí)際處理請求時 ServeletHandler就會依次調(diào)用這三個ServletHolder傳入request,response處理(實(shí)際最后交給這兩個 Filter和Servlet處理),這樣我們自己做好的一個 web應(yīng)用就掛載到這個Server上了,可以接受客戶端相應(yīng)的request(請求)。 | |
四、運(yùn)行原理(請參考如下時序圖) |
|
![]() |
|
<圖 1-7 > |
|
上圖展示了一個request的處理過程,首先HttpListener監(jiān)聽到客戶端發(fā)來的請求創(chuàng)建一個HttpConnection實(shí)例(封裝了連接細(xì)節(jié),比如從Socket連接中獲取的輸入流和輸出流), HttpConnection對象構(gòu)建過程中會創(chuàng)建Jetty內(nèi)部自定義的HttpRequest和HttpResponse對象,接著 HttpListener會調(diào)用這個HttpConnection實(shí)例的handle方法, HttpConnection實(shí)例就調(diào)用HttpRequest對象的read()方法讀取信息,調(diào)用HttpServer的service方法以 HttpRequest,HttpResponse為參數(shù)傳給HttpServer,HttpServer又將HttpRequest和 HttpResponse分發(fā)給相應(yīng)的HttpCotext,HttpContext最后將HttpRequest和HttpResponse交給自身的 HttpHandler 處理,在這里HttpRequest,HttpResponse被再次封裝為ServletHttpRequest和 ServletHttpResponse,其實(shí)這兩個類實(shí)現(xiàn)了我們所熟知的HttpServletRequest和 HttpServletResponse接口。 |
|
五、高級性能 |
|
1.HttpHandler: |
|
該接口的實(shí)現(xiàn)類用于處理HttpContext分發(fā)過來的reqeust,不同的實(shí)現(xiàn)類的有不同的處理功能,這里介紹幾常用的HttpHandler實(shí)現(xiàn)類: | |
ReourceHandler:用于處理靜態(tài)內(nèi)容,如以擴(kuò)展名為.html的文件 | |
SecurityHandler:提供基本的安全驗證 |
|
ForwardHandler:轉(zhuǎn)發(fā)一個request到另一個url |
|
ServletHandler:用于將request交由具體的Servlet類進(jìn)行處理 | |
2.當(dāng)你在看圖 1-2 時候會注意到HttpServer和HttpListener,HttpServer與HttpContext,HttpContext與 HttpHandler存在一對多的關(guān)系,下面就介紹一下它們之間的這種關(guān)系如何通過程序來配置. | |
HttpListener & HttpServer: |
|
HttpListener是所有監(jiān)聽器類的接口,如圖中的SocketListener (基于傳統(tǒng)的Socket技術(shù))就實(shí)現(xiàn)了該接口,Jetty還有其它的實(shí)現(xiàn)該接口類,如SocketChannelListener(基于NIO技術(shù))類等,HttpListener職責(zé)主要是在服務(wù)器啟動后監(jiān)聽相應(yīng)端口的來自客戶端請求并建立連接(圖 1-1 中所示用HttpConnection封裝連接細(xì)節(jié)),監(jiān)聽器可在同個IP上開啟多個端口為同一個HttpServer 進(jìn)行監(jiān)聽,所以HttpListener和HttpServer是多對一的關(guān)系,如下圖: |
|
![]() |
|
<圖 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相當(dāng)于對應(yīng)客戶端請求的URL或某個虛擬機(jī), 其子類中包含若干個HttpHandler, 當(dāng)接受到request(請求)時,HttpContext會依次(按某個預(yù)定的次序)把request交給這些HttpHandler處理,直到這個 request被標(biāo)示處理過為止, 需要注意的是這個request可能被多個HttpHandler處理,但只能有一個HttpHandler能標(biāo)示這個request已被處理過. |
|
一個典型的HttpContext有用于安全處理、靜態(tài)資源處理及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服務(wù)器軟件可以同時處理多個web application,同樣一個HttpServer可以包含多個HttpContext,如下圖可以通過同一個端口的監(jiān)聽類來映射多個 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對多網(wǎng)卡(多個IP地址,不同的主機(jī)名)的服務(wù)器也提供了很好的支持,每個 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對高并發(fā)的支持 | |
![]() |
|
<圖 1-8>
|
|
如果多用戶請求服務(wù)就會涉及到多線程的管理,如圖 1-8,Jetty中主要由ThreadPool負(fù)責(zé)管理多線程,注意其中Pool.PondLife是Pool的一個內(nèi)部接口, ThreadPool.PoolThread是ThreadPool的一個內(nèi)部線程類,我們看到Pool.PondLife和Pool存在一個聚集的關(guān)系,實(shí)際上Pool對象中存放在是一個個ThreadPool.PoolThread線程對象,當(dāng)有新用戶連接上Server時,ThreadPool就從Pool中取一個空閑的線程為當(dāng)前用戶連接服務(wù)。 | |
六、小結(jié) |
|
本文通過圖示簡要介紹了Jetty整個體系架構(gòu)和主要的組件類及服務(wù)器的啟動執(zhí)行過程,其實(shí)Jetty 通常被用來做為內(nèi)嵌的Web Server來使用,一些常見的服務(wù)器軟件,如Apache Cocoon、JBoss ,JOnAs等都會采用Jetty作為Web解決方案;另外由于Jetty在性能及穩(wěn)定性要優(yōu)于同類HTTP Server的原因,Jetty已在國外已很流行,鑒于這一點(diǎn),本文作者可以預(yù)測在不久的將來Jetty同樣也會在國內(nèi)流行開來。 |
|
附: | |
來源:http://blog.leiling.com/CALM/archive/2005/12/23/106662.aspx 作者聯(lián)系方式:陳應(yīng)剛 dycyg@yahoo.com 熊紅陽 |
posted on 2007-05-21 22:30 江天部落格 閱讀(4668) 評論(0) 編輯 收藏 所屬分類: Jetty