




已閱讀5頁(yè),還剩4頁(yè)未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
使用Jetty和DWR創(chuàng)建伸縮性Comet程序異步服務(wù)器端事件驅(qū)動(dòng)的Ajax程序很難實(shí)現(xiàn),也很難獲得伸縮性,Plilip McCarthy展示了一個(gè)有效的方式: Comet模式允許您push數(shù)據(jù)到客戶(hù)端,而且Jetty 6的Continuations API讓您的Comet程序?qū)Υ罅靠蛻?hù)端獲得高可伸縮性。您可以方便的同DWR 2使用Comet和Continuations。 隨著Ajax在Web程序開(kāi)發(fā)技術(shù)里建立了牢固的位置,出現(xiàn)了幾種常見(jiàn)的Ajax使用模式。例如,Ajax通常用于響應(yīng)用戶(hù)輸入來(lái)使用新數(shù)據(jù)修改局部頁(yè)面。但有時(shí)候,Web程序的用戶(hù)界面需要根據(jù)偶爾的異步服務(wù)器端事件來(lái)更新,而不需要用戶(hù)動(dòng)作 - 例如,在Ajax聊天程序里顯示其他用戶(hù)輸入的一條新消息。由于Web瀏覽器和服務(wù)器間的HTTP連接只能由瀏覽器建立,服務(wù)器不能推更改數(shù)據(jù)到瀏覽器。 Ajax程序有兩個(gè)解決該問(wèn)題的基本方式:瀏覽器每隔幾秒請(qǐng)求服務(wù)器來(lái)獲得更改,或者服務(wù)器維持與瀏覽器的連接并且傳遞數(shù)據(jù)。長(zhǎng)連接技術(shù)稱(chēng)為Comet。本文展示了怎樣使用Jetty服務(wù)器引擎和DWR來(lái)實(shí)現(xiàn)簡(jiǎn)單而高效的Comet Web程序。 為什么要Comet? 輪詢(xún)方式的主要缺點(diǎn)是在大量客戶(hù)端時(shí)產(chǎn)生了大量的傳輸浪費(fèi)。每個(gè)客戶(hù)端都必須有規(guī)律的請(qǐng)求服務(wù)器來(lái)獲得更改,這是服務(wù)器資源的一個(gè)重?fù)?dān)。最壞的情況是程序很少更新,例如Ajax郵件收件箱。在這種情況下,大量的客戶(hù)端輪詢(xún)是多余的,服務(wù)器僅僅簡(jiǎn)單的響應(yīng)沒(méi)有數(shù)據(jù)。 可以通過(guò)增加輪詢(xún)間隔時(shí)間來(lái)減輕服務(wù)器負(fù)荷,但是這引入了服務(wù)器事件和客戶(hù)端知曉之間的延遲。當(dāng)然,一個(gè)合理的折衷方案可以多數(shù)程序適用,并且輪詢(xún)的工作方式也可以接受。 然而,對(duì)Comet策略的呼喚來(lái)自它可感知的高效??蛻?hù)端不會(huì)產(chǎn)生輪詢(xún)方式特有的傳輸浪費(fèi),一旦事件發(fā)生,就會(huì)被發(fā)布到客戶(hù)端。但是維持長(zhǎng)連接也消耗了服務(wù)器資源。當(dāng)servlet位置持久的請(qǐng)求在等候狀態(tài)時(shí),servlet獨(dú)占一個(gè)線程。這樣傳統(tǒng)的servlet引擎就限制了Comet的伸縮性,因?yàn)榭蛻?hù)端的數(shù)量會(huì)迅速超過(guò)服務(wù)器棧可以有效處理的線程的數(shù)量。 Jetty 6有什么不同 Jetty 6設(shè)計(jì)來(lái)處理大量并發(fā)連接,它使用Java語(yǔ)言的不堵塞I/O(java.nio)庫(kù)并且使用優(yōu)化的輸出緩沖架構(gòu)。Jetty也有一個(gè)處理長(zhǎng)連接的殺手锏:一個(gè)稱(chēng)為Continuations的特性。我將用一個(gè)接收請(qǐng)求然后等待兩秒發(fā)送響應(yīng)的簡(jiǎn)單servlet來(lái)示范Continuations。然后,我將展示當(dāng)服務(wù)器擁有更多的客戶(hù)端時(shí)將發(fā)生什么。最后我將使用Continuations重新實(shí)現(xiàn)servlet,并且您將看到它們的不同。 為了讓它更簡(jiǎn)單,我將限制Jetty servlet引擎為一個(gè)單一的請(qǐng)求處理線程。列表1顯示了相關(guān)的jetty.xml配置。事實(shí)上我需要允許在ThreadPool里總共有3個(gè)線程:Jetty服務(wù)器本身使用一個(gè),HTTP連接器使用一個(gè)來(lái)監(jiān)聽(tīng)進(jìn)來(lái)的請(qǐng)求,最后剩一個(gè)線程來(lái)執(zhí)行servlet代碼。 列表1. 單一servlet線程的Jetty配置 代碼 3 0 3 下一步,為了模仿異步事件,列表2顯示了BlockingServlet的service()方法,它簡(jiǎn)單的使用Thread.sleep()調(diào)用來(lái)在完成前暫停2,000毫秒。同時(shí)它也在執(zhí)行開(kāi)始和結(jié)束時(shí)輸出系統(tǒng)時(shí)間。為了幫助區(qū)分不同請(qǐng)求的輸出,它也把一個(gè)請(qǐng)求參數(shù)作為標(biāo)識(shí)符記錄到日志。 列表2. BlockingServlet 代碼 public class BlockingServlet extends HttpServlet public void service(HttpServletRequest req, HttpServletResponse res) throws java.io.IOException String reqId = req.getParameter(id); res.setContentType(text/plain); res.getWriter().println(Request: + reqId + tstart:t + new Date(); res.getWriter().flush(); try Thread.sleep(2000); catch (Exception e) res.getWriter().println(Request: + reqId + tend:t + new Date(); 現(xiàn)在您可以觀察幾個(gè)同步請(qǐng)求下servlet的行為。列表3顯示了使用lynx的5個(gè)并行請(qǐng)求時(shí)控制臺(tái)的輸出。命令行簡(jiǎn)單的啟動(dòng)5個(gè)lynx進(jìn)程,加上一個(gè)標(biāo)識(shí)符序數(shù)到請(qǐng)求的URL。 列表3. 到BlockingServlet的幾個(gè)并發(fā)請(qǐng)求的輸出 代碼 $ for i in seq 1 5 ; do lynx -dump localhost:8080/blocking?id=$i & done Request: 1 start: Sun Jul 01 12:32:29 BST 2007 Request: 1 end: Sun Jul 01 12:32:31 BST 2007 Request: 2 start: Sun Jul 01 12:32:31 BST 2007 Request: 2 end: Sun Jul 01 12:32:33 BST 2007 Request: 3 start: Sun Jul 01 12:32:33 BST 2007 Request: 3 end: Sun Jul 01 12:32:35 BST 2007 Request: 4 start: Sun Jul 01 12:32:35 BST 2007 Request: 4 end: Sun Jul 01 12:32:37 BST 2007 Request: 5 start: Sun Jul 01 12:32:37 BST 2007 Request: 5 end: Sun Jul 01 12:32:39 BST 2007 列表3的輸出并不驚奇。由于Jetty只有一個(gè)線程來(lái)執(zhí)行servlet的service()方法,Jetty將每個(gè)請(qǐng)求列隊(duì)并按順序服務(wù)。時(shí)間戳顯示了在一個(gè)應(yīng)答分派給一個(gè)請(qǐng)求(以及end消息)后,servlet開(kāi)始處理下一個(gè)請(qǐng)求(下一個(gè)start消息)。所以即使所有的5個(gè)請(qǐng)求是同時(shí)發(fā)出的,最后的那個(gè)請(qǐng)求必須等待8秒才能得到處理。 現(xiàn)在,看看Jetty 6的Continuations特性在這種情形下是多么的有用。列表4顯示了列表2的BlockingServlet使用Continuations API重寫(xiě)后的樣子。我將在后面解釋代碼。 列表4. ContinuationServlet 代碼 public class ContinuationServlet extends HttpServlet public void service(HttpServletRequest req, HttpServletResponse res) throws java.io.IOException String reqId = req.getParameter(id); Continuation cc = ContinuationSupport.getContinuation(req, null); res.setContentType(text/plain); res.getWriter().println(Request: + reqId + tstart:t + new Date(); res.getWriter().flush(); cc.suspend(2000); res.getWriter().println(Request: + reqId + tend:t + new Date(); 列表5顯示了對(duì)ContinuationServlet作5個(gè)并發(fā)請(qǐng)求時(shí)的輸出,可以和列表3比較一下。 列表5. 到ContinuationServlet的幾個(gè)并發(fā)請(qǐng)求的輸出 代碼 $ for i in seq 1 5 ; do lynx -dump localhost:8080/continuation?id=$i & done Request: 1 start: Sun Jul 01 12:37:37 BST 2007 Request: 1 start: Sun Jul 01 12:37:39 BST 2007 Request: 1 end: Sun Jul 01 12:37:39 BST 2007 Request: 3 start: Sun Jul 01 12:37:37 BST 2007 Request: 3 start: Sun Jul 01 12:37:39 BST 2007 Request: 3 end: Sun Jul 01 12:37:39 BST 2007 Request: 2 start: Sun Jul 01 12:37:37 BST 2007 Request: 2 start: Sun Jul 01 12:37:39 BST 2007 Request: 2 end: Sun Jul 01 12:37:39 BST 2007 Request: 5 start: Sun Jul 01 12:37:37 BST 2007 Request: 5 start: Sun Jul 01 12:37:39 BST 2007 Request: 5 end: Sun Jul 01 12:37:39 BST 2007 Request: 4 start: Sun Jul 01 12:37:37 BST 2007 Request: 4 start: Sun Jul 01 12:37:39 BST 2007 Request: 4 end: Sun Jul 01 12:37:39 BST 2007 在列表5里有兩件重要的事情值得注意。首先,每個(gè)start消息出現(xiàn)了兩次,暫時(shí)不要擔(dān)心這點(diǎn)。其次,更重要的是,現(xiàn)在請(qǐng)求是并發(fā)處理的,沒(méi)有排隊(duì)。注意所有的start和end消息時(shí)間戳是一樣的。因此,沒(méi)有哪個(gè)請(qǐng)求耗時(shí)超多兩秒,即使只有單一的servlet線程在運(yùn)行。 深入Jetty的Continuations機(jī)制 理解Jetty的Continuations機(jī)制的將解釋您在列表5里看到的東西。為了使用Continuatins,Jetty必須配置為使用它的SelectChannelConnector處理請(qǐng)求。這個(gè)connector構(gòu)建在java.nio API之上,允許它維持每個(gè)連接開(kāi)放而不用消耗一個(gè)線程。當(dāng)使用SelectChannelConnector時(shí),ContinuationSupport.getContinuation()提供一個(gè)SelectChannelConnector.RetryContinuation實(shí)例(但是,您必須針對(duì)Continuation接口編程)。當(dāng)在RetryContinuation上調(diào)用suspend()時(shí),它拋出一個(gè)特殊的運(yùn)行時(shí)異常 - RetryRequest,該異常傳播到servlet外并且回溯到filter鏈,最后被SelectChannelConnector捕獲。但是不會(huì)發(fā)送一個(gè)異常響應(yīng)給客戶(hù)端,而是將請(qǐng)求維持在未決 Continuations隊(duì)列里,則HTTP連接保持開(kāi)放。這樣,用來(lái)服務(wù)請(qǐng)求的線程返回給ThreadPool,然后又可以用來(lái)服務(wù)其他請(qǐng)求。 暫停的請(qǐng)求停留在未決 Continuations隊(duì)列里直到指定的過(guò)期時(shí)間,或者在它的Continuation上調(diào)用resume()方法。當(dāng)任何一個(gè)條件觸發(fā)時(shí),請(qǐng)求會(huì)重新提交給servlet(通過(guò)filter鏈)。這樣,整個(gè)請(qǐng)求被重播直到RetryRequest異常不再拋出,然后繼續(xù)按正常情況執(zhí)行。 列表5里的輸出現(xiàn)在應(yīng)該能理解了。對(duì)每個(gè)請(qǐng)求,按順序進(jìn)入到servlet的service()方法,start消息發(fā)送給應(yīng)答,然后Continuation的suspend()方法保留servlet,然后釋放線程來(lái)開(kāi)始下一請(qǐng)求。所有的5個(gè)請(qǐng)求迅速運(yùn)行service()方法的第一部分并馬上進(jìn)入暫停狀態(tài),所有的start消息在幾毫秒內(nèi)輸出。兩秒后,suspend()過(guò)期,第一個(gè)請(qǐng)求從未決隊(duì)列里重新得到并重新提交給ContinuationServlet。start消息第二次輸出,對(duì)suspend()方法的第二次調(diào)用立即返回,然后end消息被發(fā)送給應(yīng)答。然后servlet代碼執(zhí)行下一個(gè)隊(duì)列請(qǐng)求,以此類(lèi)推。 所以,在BlockingServlet和ContinuationServlet兩種情況下,請(qǐng)求被排入隊(duì)列來(lái)訪問(wèn)單一的servlet線程。盡管如此,在BlockingServlet里的兩秒鐘暫停在servlet線程里執(zhí)行時(shí),ContinuationServlet的暫停發(fā)生于servlet外面的SelectChannelConnector里。ContinuationServlet全部的吞吐量會(huì)更高,因?yàn)閟ervlet線程不會(huì)在sleep()調(diào)用時(shí)阻礙大多數(shù)時(shí)間。 讓Continuations變得有用 現(xiàn)在您已經(jīng)看到Continuations運(yùn)行servlet請(qǐng)求暫停而不消耗線程,我需要多解釋一下Continuations API來(lái)展示怎樣使用Continuations達(dá)到特殊的目的。 一個(gè)resume()方法和一個(gè)suspend()方法配對(duì)。您可以認(rèn)為它們是標(biāo)準(zhǔn)的Object wait()/notify()機(jī)制的Continuations等價(jià)物。即,suspend()維持一個(gè)Continuation直到過(guò)期或者另一個(gè)線程調(diào)用resume()。suspend()/resume()方法是使用Continuations實(shí)現(xiàn)真實(shí)的Comet風(fēng)格服務(wù)的關(guān)鍵所在。基本的模式是從當(dāng)前請(qǐng)求維持Continuation,調(diào)用suspend(),然后等待直到您的異步時(shí)間到達(dá),然后調(diào)用resume()并生成應(yīng)答。 但是,不像編程語(yǔ)言里真實(shí)的語(yǔ)言級(jí)continuations,如Scheme,或Java語(yǔ)言里的wait()/noti
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年環(huán)保產(chǎn)業(yè)園區(qū)產(chǎn)業(yè)集聚與區(qū)域協(xié)同發(fā)展產(chǎn)業(yè)協(xié)同發(fā)展政策分析報(bào)告
- 新能源汽車(chē)充電設(shè)施布局優(yōu)化與2025年充電樁投資回報(bào)分析報(bào)告
- 2025年可持續(xù)發(fā)展目標(biāo)(SDGs)在災(zāi)害風(fēng)險(xiǎn)管理中的國(guó)際經(jīng)驗(yàn)與啟示報(bào)告
- 綠色礦山建設(shè)2025年:尾礦處理與生態(tài)修復(fù)技術(shù)創(chuàng)新實(shí)踐報(bào)告
- 2025年智慧城市商業(yè)綜合體運(yùn)營(yíng)策略?xún)?yōu)化評(píng)估報(bào)告
- 2025年醫(yī)藥行業(yè)研發(fā)外包(CRO)合作模式創(chuàng)新與行業(yè)競(jìng)爭(zhēng)力報(bào)告001
- 工業(yè)互聯(lián)網(wǎng)平臺(tái)增強(qiáng)現(xiàn)實(shí)交互技術(shù)在工業(yè)自動(dòng)化控制中的應(yīng)用研究報(bào)告
- 2025年家用醫(yī)療器械市場(chǎng)消費(fèi)需求與品牌市場(chǎng)占有率分析報(bào)告
- 2025年醫(yī)藥企業(yè)研發(fā)外包(CRO)模式下的藥物研發(fā)項(xiàng)目管理工具與實(shí)踐報(bào)告
- 2025年醫(yī)藥企業(yè)CRO模式下的研發(fā)團(tuán)隊(duì)建設(shè)與人才培養(yǎng)報(bào)告
- 柳州職業(yè)技術(shù)學(xué)院輔導(dǎo)員考試題庫(kù)
- 藥學(xué)綜合知識(shí)與技能
- 汽車(chē)維修服務(wù)清單
- 山東工商學(xué)院馬克思主義基本原理期末復(fù)習(xí)題及參考答案
- 2023-2024學(xué)年河北省武安市小學(xué)語(yǔ)文六年級(jí)期末高分提分卷附參考答案和詳細(xì)解析
- 徐州市教師業(yè)務(wù)能力測(cè)試題庫(kù)(數(shù)學(xué))
- IMC整合營(yíng)銷(xiāo)傳播培訓(xùn)教材課件
- 2023年副主任醫(yī)師(副高)-神經(jīng)內(nèi)科學(xué)(副高)歷年考試真題試卷摘選答案
- 2022年天水市武山縣社區(qū)工作者招聘考試試題
- 2022年出版專(zhuān)業(yè)資格考試中級(jí)中級(jí)出版專(zhuān)業(yè)基礎(chǔ)知識(shí)考試題
- 疼痛治療(外科學(xué)-九章)
評(píng)論
0/150
提交評(píng)論