欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
使用Jetty和DWR創(chuàng )建伸縮性Comet程序 - zhangwenjun8045 - J...

使用Jetty和DWR創(chuàng )建伸縮性Comet程序

異步服務(wù)器端事件驅動(dòng)的Ajax程序很難實(shí)現,也很難獲得伸縮性。在Java+developers:" target=blank>作者的系列文章里,Plilip McCarthy展示了一個(gè)有效的方式:
 Comet模式允許您push數據到客戶(hù)端,而且Jetty 6的Continuations API讓您的Comet程序對大量客戶(hù)端獲得高可伸縮性。您可以方便的同DWR 2使用Comet和Continuations。

 隨著(zhù)Ajax在Web程序開(kāi)發(fā)技術(shù)里建立了牢固的位置,出現了幾種常見(jiàn)的Ajax使用模式。例如,Ajax通常用于響應用戶(hù)輸入來(lái)使用新數據修改局部頁(yè)面。但有時(shí)候,Web程序的用戶(hù)界面需要根據偶爾的異步服務(wù)器端事件來(lái)更新,而不需要用戶(hù)動(dòng)作 -- 例如,在A(yíng)jax聊天程序里顯示其他用戶(hù)輸入的一條新消息。由于Web瀏覽器和服務(wù)器間的HTTP連接只能由瀏覽器建立,服務(wù)器不能"推"更改數據到瀏覽器。

 Ajax程序有兩個(gè)解決該問(wèn)題的基本方式:瀏覽器每隔幾秒請求服務(wù)器來(lái)獲得更改,或者服務(wù)器維持與瀏覽器的連接并且傳遞數據。長(cháng)連接技術(shù)稱(chēng)為Comet。本文展示了怎樣使用Jetty服務(wù)器引擎和DWR來(lái)實(shí)現簡(jiǎn)單而高效的Comet Web程序。

為什么要Comet?
 輪詢(xún)方式的主要缺點(diǎn)是在大量客戶(hù)端時(shí)產(chǎn)生了大量的傳輸浪費。每個(gè)客戶(hù)端都必須有規律的請求服務(wù)器來(lái)獲得更改,這是服務(wù)器資源的一個(gè)重擔。最壞的情況是程序很少更新,例如Ajax郵件收件箱。在這種情況下,大量的客戶(hù)端輪詢(xún)是多余的,服務(wù)器僅僅簡(jiǎn)單的響應"沒(méi)有數據"??梢酝ㄟ^(guò)增加輪詢(xún)間隔時(shí)間來(lái)減輕服務(wù)器負荷,但是這引入了服務(wù)器事件和客戶(hù)端知曉之間的延遲。當然,一個(gè)合理的折衷方案可以多數程序適用,并且輪詢(xún)的工作方式也可以接受。

 然而,對Comet策略的呼喚來(lái)自它可感知的高效??蛻?hù)端不會(huì )產(chǎn)生輪詢(xún)方式特有的傳輸浪費,一旦事件發(fā)生,就會(huì )被發(fā)布到客戶(hù)端。但是維持長(cháng)連接也消耗了服務(wù)器資源。當servlet位置持久的請求在等候狀態(tài)時(shí),servlet獨占一個(gè)線(xiàn)程。這樣傳統的servlet引擎就限制了Comet的伸縮性,因為客戶(hù)端的數量會(huì )迅速超過(guò)服務(wù)器??梢杂行幚淼木€(xiàn)程的數量。

Jetty 6有什么不同
 Jetty 6設計來(lái)處理大量并發(fā)連接,它使用Java語(yǔ)言的不堵塞I/O(java.nio)庫并且使用優(yōu)化的輸出緩沖架構。Jetty也有一個(gè)處理長(cháng)連接的殺手锏:一個(gè)稱(chēng)為Continuations的特性。我將用一個(gè)接收請求然后等待兩秒發(fā)送響應的簡(jiǎn)單servlet來(lái)示范Continuations。然后,我將展示當服務(wù)器擁有更多的客戶(hù)端時(shí)將發(fā)生什么。最后我將使用Continuations重新實(shí)現servlet,并且您將看到它們的不同。

 為了讓它更簡(jiǎn)單,我將限制Jetty servlet引擎為一個(gè)單一的請求處理線(xiàn)程。列表1顯示了相關(guān)的jetty.xml配置。事實(shí)上我需要允許在ThreadPool里總共有3個(gè)線(xiàn)程: Jetty服務(wù)器本身使用一個(gè),HTTP連接器使用一個(gè)來(lái)監聽(tīng)進(jìn)來(lái)的請求,最后剩一個(gè)線(xiàn)程來(lái)執行servlet代碼。
列表1. 單一servlet線(xiàn)程的Jetty配置

代碼

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
"http://jetty.mortbay.org/configure.dtd">
<Configure id="Server" class="org.mortbay.jetty.server">
<Set name="ThreadPool">
<New class="org.mortbay.threadBoundedThreadPool">
<Set name="minThreads">3</Set>
<Set name="lowThreads">0</Set>
<Set name="maxThreads">3</Set>
</New>
</Set>
</Configure>

 下一步,為了模仿異步事件,列表2顯示了BlockingServlet的service()方法,它簡(jiǎn)單的使用Thread.sleep()調用來(lái)在完成前暫停2,000毫秒。同時(shí)它也在執行開(kāi)始和結束時(shí)輸出系統時(shí)間。為了幫助區分不同請求的輸出,它也把一個(gè)請求參數作為標識符記錄到日志。
列表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());
}
}
 現在您可以觀(guān)察幾個(gè)同步請求下servlet的行為。列表3顯示了使用lynx的5個(gè)并行請求時(shí)控制臺的輸出。命令行簡(jiǎn)單的啟動(dòng)5個(gè)lynx進(jìn)程,加上一個(gè)標識符序數到請求的URL。
列表3. 到BlockingServlet的幾個(gè)并發(fā)請求的輸出

代碼

$ 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è)線(xiàn)程來(lái)執行servlet的service()方法, Jetty將每個(gè)請求列隊并按順序服務(wù)。時(shí)間戳顯示了在一個(gè)應答分派給一個(gè)請求(以及end消息)后,servlet開(kāi)始處理下一個(gè)請求(下一個(gè) start消息)。所以即使所有的5個(gè)請求是同時(shí)發(fā)出的,最后的那個(gè)請求必須等待8秒才能得到處理。

 現在,看看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顯示了對ContinuationServlet作5個(gè)并發(fā)請求時(shí)的輸出,可以和列表3比較一下。
列表5. 到ContinuationServlet的幾個(gè)并發(fā)請求的輸出

代碼

$ 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消息出現了兩次,暫時(shí)不要擔心這點(diǎn)。其次,更重要的是,現在請求是并發(fā)處理的,沒(méi)有排隊。注意所有的start和end消息時(shí)間戳是一樣的。因此,沒(méi)有哪個(gè)請求耗時(shí)超多兩秒,即使只有單一的 servlet線(xiàn)程在運行。

深入Jetty的Continuations機制
 理解Jetty的Continuations機制的將解釋您在列表5里看到的東西。為了使用Continuatins,Jetty必須配置為使用它的SelectChannelConnector處理請求。這個(gè) connector構建在java.nio API之上,允許它維持每個(gè)連接開(kāi)放而不用消耗一個(gè)線(xiàn)程。當使用SelectChannelConnector時(shí), ContinuationSupport.getContinuation()提供一個(gè) SelectChannelConnector.RetryContinuation實(shí)例(但是,您必須針對Continuation接口編程)。當在 RetryContinuation上調用suspend()時(shí),它拋出一個(gè)特殊的運行時(shí)異常 -- RetryRequest,該異常傳播到servlet外并且回溯到filter鏈,最后被SelectChannelConnector捕獲。但是不會(huì )發(fā)送一個(gè)異常響應給客戶(hù)端,而是將請求維持在未決 Continuations隊列里,則HTTP連接保持開(kāi)放。這樣,用來(lái)服務(wù)請求的線(xiàn)程返回給ThreadPool,然后又可以用來(lái)服務(wù)其他請求。

 暫停的請求停留在未決 Continuations隊列里直到指定的過(guò)期時(shí)間,或者在它的Continuation上調用resume()方法。當任何一個(gè)條件觸發(fā)時(shí),請求會(huì )重新提交給servlet(通過(guò)filter鏈)。這樣,整個(gè)請求被"重播"直到RetryRequest異常不再拋出,然后繼續按正常情況執行。

 列表5里的輸出現在應該能理解了。對每個(gè)請求,按順序進(jìn)入到servlet的service()方法,start消息發(fā)送給應答,然后 Continuation的suspend()方法保留servlet,然后釋放線(xiàn)程來(lái)開(kāi)始下一請求。所有的5個(gè)請求迅速運行service()方法的第一部分并馬上進(jìn)入暫停狀態(tài),所有的start消息在幾毫秒內輸出。兩秒后,suspend()過(guò)期,第一個(gè)請求從未決隊列里重新得到并重新提交給 ContinuationServlet。start消息第二次輸出,對suspend()方法的第二次調用立即返回,然后end消息被發(fā)送給應答。然后 servlet代碼執行下一個(gè)隊列請求,以此類(lèi)推。

 所以,在BlockingServlet和ContinuationServlet兩種情況下,請求被排入隊列來(lái)訪(fǎng)問(wèn)單一的servlet線(xiàn)程。盡管如此,在BlockingServlet里的兩秒鐘暫停在servlet線(xiàn)程里執行時(shí),ContinuationServlet的暫停發(fā)生于 servlet外面的SelectChannelConnector里。ContinuationServlet全部的吞吐量會(huì )更高,因為servlet 線(xiàn)程不會(huì )在sleep()調用時(shí)阻礙大多數時(shí)間。

讓Continuations變得有用
 現在您已經(jīng)看到Continuations運行servlet請求暫停而不消耗線(xiàn)程,我需要多解釋一下Continuations API來(lái)展示怎樣使用Continuations達到特殊的目的。

 一個(gè)resume()方法和一個(gè)suspend()方法配對。您可以認為它們是標準的Object wait()/notify()機制的Continuations等價(jià)物。即,suspend()維持一個(gè)Continuation直到過(guò)期或者另一個(gè)線(xiàn)程調用resume()。suspend()/resume()方法是使用Continuations實(shí)現真實(shí)的Comet風(fēng)格服務(wù)的關(guān)鍵所在?;镜哪J绞菑漠斍罢埱缶S持Continuation,調用suspend(),然后等待直到您的異步時(shí)間到達,然后調用resume()并生成應答。

 但是,不像編程語(yǔ)言里真實(shí)的語(yǔ)言級continuations,如Scheme,或Java語(yǔ)言里的wait()/notify(),在 Jetty Continuation上調用resume()并不意味著(zhù)代碼執行于它停止的確切位置。您已經(jīng)看到,真正發(fā)生的是與Continuation相關(guān)的請求被重播。這導致兩個(gè)問(wèn)題:列表4的ContinuationServlet里代碼不合需要的重新執行,以及丟失狀態(tài) -- 暫停時(shí)作用域里的任何東西都丟失了。

 第一個(gè)問(wèn)題的解決方案是isPending()方法,如果isPending()方法的返回值為true,這意味著(zhù)suspend()在前面已經(jīng)被調用過(guò)了,并且二次請求的執行不會(huì )再次接觸suspend()方法。換句話(huà)說(shuō),給您的suspend()調用前的代碼加上isPending()條件可以確保它只被執行一次。Continuation也提供了一個(gè)簡(jiǎn)單的機制來(lái)保持狀態(tài):putObject(Object)和getObject()方法。使用它們來(lái)維持一個(gè)context對象,這樣當Continuation暫停時(shí)任何您需要維持的狀態(tài)都可以得到保護。您也可以使用該機制作為一種在線(xiàn)程之間傳遞事件數據的方法,后面您將看到。

寫(xiě)一個(gè)基于Continuations的程序

 作為一個(gè)真實(shí)世界里的例子,我將開(kāi)發(fā)一個(gè)基本的GPS坐標跟蹤Web程序。它將在無(wú)規律間隔內生成隨機的緯度-經(jīng)度對。假設生成的坐標可以為附近的公眾移動(dòng)位置,如拿著(zhù)GPS設備馬拉松運動(dòng)員,成隊的汽車(chē),或者運輸中的包裹位置。有意思的部分在于我怎樣告訴瀏覽器坐標信息。圖1顯示了這個(gè)簡(jiǎn)單的GPS跟蹤程序的類(lèi)圖:
圖1. 顯示GPS跟蹤程序主要組件的類(lèi)圖

 首先,該程序需要生成坐標的一些東西,這是RandomWalkGenerator的工作。從一個(gè)初始坐標開(kāi)始,每次對它的私有方法 generateNextCoord()的調用都從該位置隨機走一步并返回一個(gè)GpsCoord對象。當初始化時(shí), RandomWalkGenerator創(chuàng )建一個(gè)線(xiàn)程,該線(xiàn)程在隨機間隔內調用generateNextCoorld()方法并發(fā)送生成的坐標給任何使用 addListener()注冊自己的CoordListener實(shí)例。
列表6顯示了RandomWalkGenerator的循環(huán)邏輯:
列表6. RandomWalkGenerator的run()方法

代碼

public void run() {

try {
while (true) {
int sleepMillis = 5000 + (int)(Math.random()*8000d);
Thread.sleep(sleepMillis);
dispatchUpdate(generateNextCoord());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

 CoordListener是一個(gè)定義了onCoord(GpsCoord coord)方法的回調接口。在例子中,ContinuationBasedTracker類(lèi)實(shí)現了CoordListener。 ContinuationBasedTracker的另外一個(gè)方法為getNextPosition(Continuation, int)。列表7顯示了這些方法的具體實(shí)現:
列表7. ContinuationBasedTracker的內臟

代碼

public GpsCoord getNextPosition(Continuation continuation, int timeoutSecs) {

synchronized(this) {
if (!continuation.isPending()) {
pendingContinuations.add(continuation);
}

// wait for next update
continuation.suspend(timeoutSecs*1000);
}

return (GpsCoord)continuation.getObject();
}

public void onCoord(GpsCoord gpsCoord) {

synchronized(this) {
for (Continuation continuation : pendingContinuations) {

continuation.setObject(pgsCoord);
continuation.resume();
}

pendingContinuations.clear();
}
}

當客戶(hù)端在Continuation里調用getNextPosition()時(shí),isPending()方法檢查這次請求不是重試,然后添加它到一個(gè)等待坐標的Continuations集合里,然后Continuation被暫停。同時(shí),onCoord -- 當生成新坐標時(shí)調用 -- 簡(jiǎn)單的循環(huán)每個(gè)未決Continuations,為它們設置GPS坐標,然后恢復它們。然后每個(gè)重試的請求完成getNextPosition()的執行,從Continuation得到GpsCoord并返回它給調用者。注意這里需要同步,不僅預防pendingContinuations集合里出現不一致的狀態(tài),也確保了新添加的Continuation在它被暫停之前不會(huì )被恢復。

謎題最后一部分是servlet代碼本身,顯示于列表8:
列表8. GPSTrackerServlet實(shí)現

代碼

public class GpsTrackerServlet extends HttpServlet {

private static final int TIMEOUT_SECS = 60;
private ContinuationBasedTracker tracker = new ContinuationBasedTracker();

public void service(HttpServletRequest req, HttpServletResponse res) throws java.io.IOException {

Continuation c ContinuationSupport.getContinuation(req, null);
GpsCoord position = tracker.getNextPosition(c, TIMEOUT_SECS);

String json = new Jsonifier().toJson(position);
res.getWriter().print(json);
}
}

 您可以看到,servlet所做很少。它簡(jiǎn)單的維持請求的Continuation,調用 getNextPosition(),轉換GPSCoord為JavaScript Object Notation(JSON)并輸出。這里不需要防止任何代碼重執行,所以我不需要檢查isPending()。列表9顯示了對 GpsTrackerServlet的調用的輸出,使用服務(wù)器可得到的單一線(xiàn)程上的5個(gè)并發(fā)請求。
列表9. GPSTrackerServlet輸出

代碼

$ for i in 'seq 1 5' ; do lynx -dump localhost:8080/tracker & done
{ coord : { lat : 51.51122, lng : -0.08103112 } }
{ coord : { lat : 51.51122, lng : -0.08103112 } }
{ coord : { lat : 51.51122, lng : -0.08103112 } }
{ coord : { lat : 51.51122, lng : -0.08103112 } }
{ coord : { lat : 51.51122, lng : -0.08103112 } }

 這個(gè)例子不是很引人注目但卻是概念的證明。在請求分派后,它們被維持開(kāi)發(fā)幾秒鐘直到生成坐標,這時(shí)迅速產(chǎn)生應答。這是Comet模式的基本原理,使用Jetty單一線(xiàn)程處理5個(gè)并發(fā)請求,感謝Continuations。

創(chuàng )建一個(gè)Comet客戶(hù)端
 現在您已經(jīng)看到Continuations怎樣用于創(chuàng )建非阻塞Web服務(wù),您可能想知道怎樣創(chuàng )建客戶(hù)端代碼來(lái)使用它。一個(gè)Comet客戶(hù)端需要:
1. 維持一個(gè)XMLHttpRequest連接直到接收應答.
2. 分派應答給合適的JavaScript處理者.
3. 立即建立一個(gè)新連接.
 更高級的Comet可以在客戶(hù)端和服務(wù)器使用合適的路由機制來(lái)使用一個(gè)連接來(lái)從多個(gè)不同的服務(wù)推數據到瀏覽器。一個(gè)可能性為使用JavaScript庫如Dojo等寫(xiě)客戶(hù)端代碼來(lái)提供基于Comet的請求機制,形如dojo.io.comet。

 盡管如此,如果您正在使用Java作為服務(wù)器語(yǔ)言,在客戶(hù)端和服務(wù)器端得到高級Comet支持的更好的方式是使用DWR 2。如果您不熟悉DWR,您可以該系列的第3部分,"Ajax with Direct Web Remoting"。DWR透明的提供一個(gè)HTTP-RPC傳輸層,暴露您的Java對象來(lái)使用JavaScript代碼調用。DWR生成客戶(hù)端代理,自動(dòng)marshall和unmarshall數據,處理安全問(wèn)題,提供一個(gè)便利的客戶(hù)端輔助庫,并且對所有主要的瀏覽器工作。

DWR 2:反轉Ajax
 DWR 2新引入的概念為反轉Ajax。該機制將服務(wù)端事件"推"給客戶(hù)端??蛻?hù)端DWR代碼透明的處理連接建立和應答解析,所以從開(kāi)發(fā)人員的角度來(lái)看,事件可以從服務(wù)端Java代碼簡(jiǎn)單的發(fā)布到客戶(hù)端。

 DWR可以配置使用3個(gè)不同的機制來(lái)反轉Ajax。一種是我們熟悉的輪詢(xún)方式。第二種方式稱(chēng)為piggyback,它不創(chuàng )建任何到服務(wù)器的連接,而是等待直到另一個(gè)DWR服務(wù)調用發(fā)生并piggyback未決事件到該請求應答。這可以獲得高效率但是意味著(zhù)客戶(hù)端事件通知被延遲直到客戶(hù)端作出一個(gè)不相干的調用。最后一種機制使用Comet風(fēng)格的長(cháng)連接。最好的是,當DWR運行在Jetty下并且使用Continuations來(lái)獲得非阻塞Comet 時(shí)可以自動(dòng)檢測事件。

 我將修改我的GPS例子來(lái)使用DWR 2反轉Ajax。同時(shí),您將看到反轉Ajax怎樣工作的更多細節。

 我不再需要我的servlet。DWR提供了一個(gè)controller servlet,它協(xié)調客戶(hù)端請求直接訪(fǎng)問(wèn)Java對象。我也不再需要顯示處理Continuations,因為DWR在幕后處理了這些。所以我只需要一個(gè)新的CoordListener實(shí)現來(lái)發(fā)布坐標更新到任何客戶(hù)端瀏覽器。

 一個(gè)稱(chēng)為ServerContext的接口提供DWR的反轉Ajax魔法。ServerContext知道當前查看一個(gè)給定頁(yè)面的所有Web客戶(hù)端并且可以提供一個(gè)ScriptSession來(lái)與每個(gè)客戶(hù)端交流。ScriptSession用來(lái)從Java代碼推JavaScript片段到客戶(hù)端。列表10顯示了ReverseAjaxTracker怎樣響應坐標通知,以及使用它們來(lái)生成客戶(hù)端updateCoordinate()方法調用。注意如果一個(gè)合適的轉換器是可用的,則DWR的ScriptBuffer對象的appendData()調用會(huì )自動(dòng)marshall一個(gè)Java對象到 JSON,。
列表10. ReverseAjaxTracker里的通知回調方法

代碼

public void onCoord(GpsCoord gpsCoord) {

// Generate JavaScriptcode to call client-side
// function with coord data
ScriptBuffer script = new ScriptBuffer();
script.appendScript("updateCoordinate(").appendData(gpsCoord).appendScript(");");

// Push script out to clients viewing the page
Collection<ScriptSession> sessions = sctx.getScriptSessionsByPage(pageUrl);

for (ScriptSession session : sessions) {
session.addScript(script);
}
}

 下一步,DWR必須配置來(lái)知道ReverseAjaxTracker。在更大的程序里,DWR的 Spring集成可以使用Spring創(chuàng )建的beans來(lái)提供DWR。但是這里,我將僅僅讓DWR創(chuàng )建一個(gè)新的ReverseAjaxTracker實(shí)例并把它放在application作用域里。所有后續的DWR請求將訪(fǎng)問(wèn)這個(gè)單一的實(shí)例。

 我也需要告訴DWR怎樣從GpsCoord beans來(lái)marshall數據到JSON。由于GpsCoord是一個(gè)簡(jiǎn)單對象,DWR基于反射的BeanConverter足夠。
列表11顯示了ReverseAjaxTracker配置。
列表11. ReverseAjaxTracker的DWR配置

代碼

<dwr>
<allow>
<create creator="new" javascrit="Tracker" scope="application">
<param name="class" value="developerworks.jetty6.gpstracker.ReverseAjaxTracker"/>
</create>

<convert converter="bean" match="developerworks.jetty6.gpstracker.GpsCoord"/>
</allow>
</dwr>

create元素的javascript元素指定了DWR用來(lái)暴露tracker作為一個(gè)JavaScript對象的名字。但是,在這里,我的客戶(hù)端代碼不會(huì )使用它,而是從tracker推數據給它。同時(shí),也需要在web.xml里做一些額外的配置來(lái)讓DWR使用反轉Ajax,見(jiàn)列表12。
列表12. DwrServlet的web.xml配置

代碼

<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>
org.directwebremoteing.servlet.DwrServlet
</servlet-class>
<init-param>
<param-name>activeReverseAjaxEnabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>initApplicationScopeCreatorsAtStartup</param-name>
<param-value>true</param-value>
</init-param>
</servlet>

 第一個(gè)servlet init-param,activeReverseAjaxEnabled,激活輪詢(xún)和Comet功能。第二個(gè), initApplicationScopeCreatorsAtStartup,告訴DWR當程序開(kāi)始時(shí)初始化ReverseAjaxTracker。這會(huì )覆蓋通常在bean上作第一次請求時(shí)的延遲初始化行為 -- 在這里這是很有必要的,因為客戶(hù)端從不在ReverseAjaxTracker上調用方法。

 最后,我需要實(shí)現從DWR調用的客戶(hù)端JavaScript方法?;卣{方法updateCoordinate()被傳遞一個(gè)JSON形式的 GpsCoord對象,它由DWR的BeanConverter自動(dòng)序列化。這個(gè)方法僅僅從坐標提取longitude和latitude域并通過(guò)DOM 調用添加它們到一個(gè)列表里。這在列表13里顯示了,同我的頁(yè)面的onload方法一起。onload包含對 dwr.engine.setActiveReverseAjax(true),這告訴DWR打開(kāi)一個(gè)到服務(wù)器的持久的連接來(lái)等待回調。
列表13. 反轉Ajax GPS跟蹤的客戶(hù)端實(shí)現

代碼

window.onload = function() {
dwr.engine.setActiveReverseAjax(true);
}

function updateCoordinate(coord) {
if (coord) {
var li = document.createElement("li");
li.appendChild(document.createTextNode(coord.longitude + ", " + coord.latitude));
document.getElementById("coords").appendChild(li);
}
}

 現在我可以讓我的瀏覽器訪(fǎng)問(wèn)跟蹤程序頁(yè)面,當坐標數據開(kāi)始生成時(shí)DWR將開(kāi)始推數據到客戶(hù)端。這個(gè)實(shí)現將簡(jiǎn)單的輸出一個(gè)生成的坐標列表,見(jiàn)圖2:
圖2. ReverseAjaxTracker輸出

 使用反轉Ajax創(chuàng )建一個(gè)事件驅動(dòng)的Ajax程序是如此簡(jiǎn)單。記住,感謝DWR對Jetty Continuations的使用,當等待新事件到達時(shí)線(xiàn)程不會(huì )阻塞在服務(wù)器。

 據此,很容易從Yahoo!或者Google集成一個(gè)地圖窗口部件。通過(guò)改變客戶(hù)端回調方法,坐標可以簡(jiǎn)單的傳遞到地圖API,而不是直接添加到頁(yè)面。圖3顯示了在這樣的一個(gè)地圖組件上DWR反轉Ajax GPS跟蹤程序描繪的隨機路線(xiàn):
圖3. 使用地圖UI的ReverseAjaxTracker

結論
 現在您看到了Jetty Continuations聯(lián)合Comet可以提供一個(gè)高效的、可伸縮的事件驅動(dòng)Ajax程序的解決方案。我沒(méi)有給出Continuations的伸縮性的圖,因為性能在真是世界里取決于許多變數。服務(wù)器硬件、操作系統的選擇、JVM實(shí)現、Jetty配置、您的Web程序的設計和傳輸效率在負荷下都會(huì )影響 Jetty Continuations的性能。盡管如此,Webtide的Greg Wilkins(首要的Jetty開(kāi)發(fā)者) 發(fā)布了一個(gè)比較Jetty 6集成Continuations與不集成Continuations的Comet程序處理10,000并發(fā)請求時(shí)的性能的白皮書(shū)。在Greg的測試里,使用Continuations并去掉了線(xiàn)程消費和棧內存消費,使用大于10的因數。

 您也看到了使用DWR的反轉Ajax技術(shù)實(shí)現事件驅動(dòng)的Ajax程序是多么容易。DWR不僅節省您的客戶(hù)端和服務(wù)端代碼,反轉Ajax也將整個(gè)服務(wù)器推機制從您的代碼中抽象出來(lái)。您可以隨意轉換您的Comet方式:輪詢(xún)或者piggyback方式,只需簡(jiǎn)單的更改DWR配置。您可以隨意試驗并找到適合您的程序的最佳策略而不會(huì )影響您的代碼。

關(guān)于作者
Philip McCarthy是倫敦的一位軟件開(kāi)發(fā)顧問(wèn),專(zhuān)于Java和Web技術(shù)。
  • 描述:
  • 大小: 98.5 KB
  • 查看次數: 0
  • 描述:
  • 大小: 77.2 KB
  • 查看次數: 0
  • 描述:
  • 大小: 11.1 KB
  • 查看次數: 1
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
面向 Java 開(kāi)發(fā)人員的 Ajax: 使用 Jetty 和 Direct Web Rem...
Comet簡(jiǎn)介
Jetty 服務(wù)器的并發(fā)處理
服務(wù)器推送技術(shù) - ServerPush
為 WebSphere Application Server Community Edit...
DWR高級主題之反向Ajax(DWR3介紹)
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久