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