Servlet3.0的異步處理

Java servlet是大家公認的服務器端web技術的標準,包括jsp,jsf,和大量的web框架,soap,RESTful web service api ,還有新聞供應。servlet運行在這些技術下面,以保證這些技術可以運行在任何java web服務器上。所以servlet的任何變化都會對所有與服務器有關的技術產生影響。

已經在2009年1月通過公衆審覈的servlet3.0,是一個包含很多重要特性的最新版本,它的出現將會給java web開發者帶來更好的體驗。下面就是servlet3.0所包含的新的特性。

1,支持異步處理。

2,配置簡單。

3,可插入性。

4,增強既存api。

支持異步處理是servlet3.0的最重要的特徵,它將更便捷服務器端ajax application的開發,以下將重點講述servlet在支持異步處理上所做的處理,主要是連接和線程消耗。以及當今應用異步處理的實現,例如:comet和reverse ajax。最後還將介紹有關servlet3.0的其他特性。

異步支持:背景概念

web2.0迅速地改變了服務器與客戶端的通信方式,servlet3.0的支持異步處理正是基於這種變化的迴應。爲了徹底地理解異步處理,首先我們先回顧一下http通信的進化史。

http1.0和http1.1

http1.1標準的一個重要的改善部分是能夠建立持續的鏈接,在http1.0時代,服務器與客戶端的通信在一次請求和迴應後就會被關閉,但是在http1.1標準下,在多次請求的過程中,鏈接能夠持續保持和可用。持續的連接減少了網絡連接緩慢的可見狀態,因爲客戶端不必在每次請求後都重新建立tcp連接。

基於連接的線程

解決怎樣使服務器能更具有擴展性是現在許多研究人員正在進行的課題,基於http1.1持續連接的http連接線程,是研究人員們所採用的一個通用的解決方案,在這個策略下,每一個客戶端與服務器端的連接都與一個服務器端的線程有聯繫,線程由一個服務器控制的線程池來管理,一旦一個連接關閉,負責這個連接的線程將會被收回,並重新放回線程池以備其他連接使用。依靠硬件配置,這種方式可以處理大量的並行連接,有試驗表明,服務器端的內存消耗會隨着連接數量的增加成比例的增長,原因很簡單,線程與內存使用是成比例的。所以配置了固定數量線程的服務器會產生線程死鎖現象,因此一旦服務器線程池裏的線程全部被佔用,來自客戶端的請求就會被拒絕。另外,有很多的站點,用戶請求服務器頁面的機會很少,服務器連接線程在很長時間都處於閒置狀態,這也是一種資源的浪費。

基於請求的線程

由於在java4的nio中新的i/o api中出現的無阻塞(non-blocking i/o)i/o處理能力,把原來的一個連接需要附加一個線程的處理方式變成只有當該請求被處理的時候才被分派線程的處理方式。當連接在請求的過程中處於閒置狀態時,它會被放置到一箇中央選擇處理集合裏,來觀測新的請求而不是消耗一個獨立的線程。這種模式被稱爲基於請求的進程。潛在的允許服務器在固定線程的基礎上處理不斷增長的用戶連接。在相同硬件條件下,這種方式比基於連接進程方式擴展的更好。 現在很多web服務器,如:tomcat,jetty,glassfish,weglogic,websphere都通過java NIO應用這種方式。對於應用程序開發人員,這些都是以隱藏的方式進行的,不會通過servlet api暴露出來。

迎接ajax挑戰

爲了提供更豐富的用戶體驗和交互,越來越多的web application應用了ajax,應用ajax應用的用戶會獲得更多的與服務器的交互,不同於以往請求的是,ajax用戶請求可以更頻繁的被髮送到服務器端,另外,客戶端和腳本也在促使着服務器技術的更新,越來越多的同步請求 導致大量的線程被消耗,這使得基於請求線程方式的優點幾乎銷蝕殆盡。

運行緩慢,有限的資源

有些運行緩慢的後臺常規工作更加惡化這個形勢,例如:有些請求可能因爲已經廢棄的jdbc連接池或者低通量服務器端的原因而被阻塞,等待請求的線程可能被阻止很長時間直到資源再次可用。最好的辦法是把請求放到一箇中央集權的隊列中等待可用資源,並回收不可用的線程。這種方式有效地協調了大量請求和運行緩慢的後臺程序。它同時也暗示了在某種程度上放在隊列裏的請求是沒有任何線程與之對應的。servlet3.0的支持異步通信正是爲了在一個廣泛的可移植的方式上(不管是否應用ajax)實現這個目標。下面的代碼將闡述它是怎樣實現的:
@WebServlet(name="myServlet", urlPatterns={"/slowprocess"}, asyncSupported=true)
public class MyServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response) {
AsyncContext aCtx = request.startAsync(request, response);
ServletContext appScope = request.getServletContext();
((Queue<AsyncContext>)appScope.getAttribute("slowWebServiceJobQueue")).add(aCtx);
}
}

@WebServletContextListener
public class SlowWebService implements ServletContextListener {

public void contextInitialized(ServletContextEvent sce) {
Queue<AsyncContext> jobQueue = new ConcurrentLinkedQueue<AsyncContext>();
sce.getServletContext().setAttribute("slowWebServiceJobQueue", jobQueue);
// pool size matching Web services capacity
Executor executor = Executors.newFixedThreadPool(10);
while(true)
{
if(!jobQueue.isEmpty())
{
final AsyncContext aCtx = jobQueue.poll();
executor.execute(new Runnable(){
public void run() {
ServletRequest request = aCtx.getRequest();
// get parameteres
// invoke a Web service endpoint
// set results
aCtx.forward("/result.jsp");
}
});
}
}
}

public void contextDestroyed(ServletContextEvent sce) {
}
}

在這段代碼,當asyncSupported 屬性被設置爲true時,response對象還沒有被賦給exit方法。調用startAsync方法返回一個AsyncContext對象,它用來緩存request和response對象,該AsyncContext對象被存放在一個application域隊列裏,緊接着,doGet方法執行並返回,原始的request線程被回收,在ServletContextListener對象,在application開始監視上述隊列並隨時回收request處理過程,在request對象被處理後,你有選擇調用ServletResponse.getWriter.print()方法然後調用complete方法來提交Response,或者是調用forward方法來導向一個jsp來顯示結果,注意這些jsp頁面是帶有被設置爲false的asyncSupported屬性的servlets。

另外,在servlet3.0中的AsyncEvent和AsyncListener類可以給開發者對於異步生命週期事件精心的控制,你可以通過ServletRequest.addAsyncListener方法來註冊一個AsyncListener類,在startAsync方法被調用後,當異步處理結束或者過期後,一個AsyncEvent被立即發送給上述已註冊的AsyncListener類。AsyncEvent也像AsyncContext對象一樣包含一個request和response對象。

服務器端推技術

servlet3.0異步特徵的一個更重要,更有趣的應用案例就是服務器端推技術。Gtalk(一種可以使Gmail用戶在線聊天的小工具)就是使用服務器端推技術的案例,Gtalk不經常訪問服務器去核對是否有可以顯示的新信息。相反,它等待服務器向回彈推新信息,這種方式有兩種顯著的優點:不用頻繁發送request的低滯後通信,不浪費服務器資源和網絡帶寬。

ajax技術允許用戶可以在處理很多請求的同時通過頁面與服務器進行交互,一個普遍的用途就是用ajax來訪問服務器來獲得更新的狀態而不打斷用戶操作,但是高頻率訪問會浪費服務器資源和網絡帶寬,如果服務器可以動態向瀏覽器彈推數據,換句話說,通過事件向客戶端傳送異步信息(狀態改變)。ajax application將會表現的更優雅並且節省珍貴的服務器資源和網絡帶寬。

http協議是一個請求回覆的協議,一個客戶端發送請求信息到服務器端,服務器通過一個回覆信息回覆客戶端。服務器不能初始化客戶端或者發送一個非期望的信息給客戶端。http協議的這個方面使得服務器彈推信息看起來不可能,但是很多有創意的技術可以繞開這個限制。

service streaming技術,允許一個服務器通過事件觸發而不是從客戶端發送過來的請求來向客戶端發送消息,實際的實現方法是:客戶端通過一個請求來初始化一個連接,回覆信息會在服務器端事件發生的時候被一點一點地返回,理論上,回覆信息永久持續,這些信息片可以被客戶端瀏覽器接收,解析並顯示在瀏覽器上。

long polling技術,也叫異步訪問,是一種純server push and client pull技術,這種技術基於Bayeux協議,像service streaming 一樣,客戶端通過請求與服務器建立一個連接,服務器保持該請求直到一個事件的發生,一旦事件發生,一個完整的回覆信息被返回給客戶端。一接收到回覆信息,客戶端立即發送一個新的信息,這樣服務器端幾乎一直持有一個通過服務器端事件觸發回覆消息的請求。long polling技術比service streaming 更容易在瀏覽器上實現。

passive piggyback技術,當服務器有更新信息需要發送給客戶端時,它等待來自客戶端的下一個請求,一旦接收到下一個請求,它會把該更新信息連同客戶端期望的回覆信息一併發送給客戶端。

service streaming和long polling 技術通過ajax實現,以Comet 或者reverse ajax最爲著名。

ajax改善了單用戶響應性,像comet一樣的服務器端推技術使得應用程序響應更具有協作性,多用戶形式,而且不佔用正常訪問的消耗。

客戶端方的技術,如:隱藏的iframe,xmlhttprequest,dojo,jquery類庫不在本文討論範圍內,我們要探討的是servlet3.0怎樣幫助實現服務器端推技術。

有效線程利用:一個更廣泛的解決方案。

comet一直佔用一個等待在服務器端的管道返回狀態更新信息,這個管道不可以再被客戶端用來發送原始請求,所以comet實際上是在一個客戶身上建立了兩個連接,如果應用程序保持在基於請求的線程方式,每個連接都會被關聯一個特殊線程,這就導致了線程數大於實際用戶數。應用基於請求線程的comet應用只能在有限的線程消耗範圍內擴展。

未完待續。。。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章