关于Servlet和异步Servlet
Servlet API是Java EE標準的一部分,自1998年正式發布2.1規范以來,一直是基于Java的企業體系結構的重要組成部分。
它是一種自以為是的API,用于服務圍繞一些基本概念構建的請求/響應協議:
- 兼容的容器 ,這是一個專用的運行時,可以是獨立服務器(過去更常見),也可以是基于庫的嵌入式運行時(如今更常見)。 它可以支持一次托管多個Web應用程序,并在它們之間隔離類加載。 它還可以提供管理功能,例如應用程序部署,啟動,停止,資源分配,JNDI命名上下文,帶有連接池的JDBC數據源,HTTP適配器,線程池等。 它基本上是Java EE功能的集中管理軟件包,可以刪除兼容的應用程序。
- 一個或多個servlet ,即實現Servlet接口的類,它不是特定于HTTP的,因為Servlet規范通常是為請求/響應協議設計的。 實現接口意味著處理大多數容器已經處理過的servlet配置信息,因此擴展作為規范一部分的抽象類(如GenericServlet甚至HttpServlet )更為普遍(方便)。 除了生命周期管理外,其余要實現的方法是請求處理程序 ,當請求進入時,容器將調用該請求處理程序 ,并應為它們提供服務。 他們將通過處理從容器本身作為參數接收的可變請求和響應對象(也包括標準接口)來實現此目的,或者通過在發生某些意外情況時引發異常來進行處理,容器將根據配置方式對其進行適當的管理,例如,通過重定向到JSP頁面。 它們還可以包括處理并將處理(的一部分)委托給一個全新的處理鏈,該處理鏈通過RequestDispatcher映射到一些不同的URL。 這是作為鏈接servlet的一種機制,主要在2.3引入過濾器之前使用。
- 一個或多個過濾器 ,擴展了Filter接口,并且與servlet類似,不同之處在于它們支持鏈接 ,即它們按順序排列,可以將請求處理(的一部分)委托給鏈中的下一個過濾器,并執行發布-處理完成時。 Servlet始終位于過濾器鏈的末尾。
- 設置信息 ,例如請求和過濾器到HTTP請求的映射,可以通過多種方式提供,從XML描述符到類注釋再到實際的初始化代碼。
- 請求服務線程 :每個請求由一個專用線程服務,該線程將運行該請求本身已映射到的整個篩選器鏈,并阻塞與HTTP請求和響應相關的網絡I / O操作以及任何其他線程完成請求處理所需的-blocking調用。
剖析Servlet API
如果我們試圖表征長期存在的Servlet API,則可以將其限定為:
- 絕對涉及對象,因為涉及的每個概念(無論多么抽象)都已被對象化并轉換為接口或類。 “ Servlet”,“ Filter”,“ RequestDispatcher”都是這種建模風格的示例。 唯一的例外是容器本身,它是幕后的無處不在的參與者,但沒有唯一的表示形式,可以通過顯式參與者或諸如上下文之類的次要對象進行間接處理。
- 它是基于(面向對象)模式的 ,我們可以識別其中的許多 模式 。
- 它具有狀態機語義 ,它是有狀態的 ,并且是可變的,因為假設請求處理過程處于某種狀態(這是所有API的對象化參與者的狀態的總和,包括容器),那么會有一些操作將其轉換為一種新的,可部分檢查的不同狀態,同時禁止其他轉換(例如,在提交響應后轉發請求)。
- 它是基于處理程序的,因為您(開發人員)在感覺舒適時不會請求傳入的請求,而是將它們推送到您的servlet上,您不得不將它們編碼為目標化的請求處理程序。
- 它是低級的,因為它不提供路由機制,也沒有建立特定的范例,例如MVC。
- 它最初是同步產生的,因為處理程序應該在調用計算上下文(堆棧)內完成請求處理,而根本不打算延遲請求。
- 它明確地基于線程,因為規范指出處理程序的計算上下文是servlet容器線程。 同步和基于線程的結合在一起基本上意味著Servlet API最初被設計為線程阻塞的 。
總而言之,它是一個非常復雜且自以為是的API,盡管它基于非常普遍的觀點并且具有很長的向后兼容歷史。
順便說一句:Clojure的戒指,HTTP服務器的全新明晰和簡約視圖
盡管Clojure社區非常多元化,并且在每個領域都有很多非常有趣的選擇,但事實上的Clojure低級,基本的網絡標準框架是Ring 。
鑒于HTTP幾乎是一個無狀態的請求-響應協議,HTTP請求服務自然是一個非常適合于功能性,輸入-輸出建模風格的領域。 實際上,Ring認為HTTP請求總共具有3個具有直接關系的功能實體:
- 處理程序是一個函數,它接收唯一具有眾所周知的鍵名和值類型的Clojure映射作為輸入,表示HTTP請求,并生成另一個Clojure映射作為其輸出,該Clojure映射必須具有表示HTTP響應的特定結構(這是不過,這過于簡化了,因為Ring允許返回更簡單的數據結構以方便使用)。
- 中間件是一個接收處理程序功能并生成另一個處理程序功能的功能。 因此,中間件是一種高階函數,旨在以某種特定方式豐富任何處理程序的邏輯,例如攔截和處理文件系統請求或使用多部分預處理信息來豐富請求本身,因此類似于Servlet過濾器,盡管通過一流的函數之類的函數式編程思想使其變得更加簡單。 請注意,可以通過功能組合的直接方式將中間件按特定順序鏈接 ,因為將中間件應用于處理程序所獲得的是另一個處理程序,然后可以將多個中間件功能應用于該處理程序。
- 適配器是一個函數,它接收處理程序函數作為其主要輸入,并且不返回任何內容。 它的目的純粹是產生一些HTTP服務器的副作用,該HTTP服務器將使用提供的處理程序來處理請求,因此實際上是某些先前存在的(或新的)HTTP服務器技術的適配器。 它的功能接口不是標準的,因為它可以接收的輸入在很大程度上取決于技術,但是一個常見的模式是許多適配器將處理程序作為第一個參數來接收,然后將與實現相關的選項映射(或其他序列)作為第一個參數。第二個。 此外,最常用的選項(例如偵聽接口和端口)在大多數適配器中往往具有相同的鍵名。
Ring也是一種自以為是的API,并且在某些方面它并不偏離流行的概念,例如,它仍然是基于處理程序的,盡管適配器只是功能的想法使得將其用作嵌入式HTTP非常簡單。完全傳統應用程序的邊界”; 加上它是同步的, 這非常好,因為它使代碼簡單易維護。 仍然需要對主題保持一種新鮮,清晰和簡約的觀點,嘗試完全消除偶發的復雜性,并提供最少數量的正交概念,以利用功能編程思想和動態語言靈活性來簡潔有效地處理領域的內在復雜性。為了這個 這種方法非常符合Clojure語言本身的精神。
請注意,Ring對執行上下文一無所知:對于某人而言,基于輕量級光纖而不是重量級線程來為其阻塞API實現適配器是完全可以的:這正是Comsat提供的,Ring的清晰和極簡性極大地簡化了此類編寫整合。
Servlet 3.0異步
Servlet規范的Async附加示例說明了以下事實:OOP不一定簡化有狀態API的復雜性。 有時相反,它只能通過在表中散布狀態,將其拆分并放入對象中來提供這樣做的危險幻覺。
這種錯覺實際上會使情況變得更糟,因為它使我們認為,開發有狀態API的看似非常簡單的想法確實可以在沒有意外后果的情況下工作。
Servlet 3.0中 Async功能背后的“簡單”思想是一種新的請求模式,即異步請求模式 。 當通過startAsync方法調用將請求切換為異步時,我們“簡單地”告訴容器,只要請求處理鏈(過濾器和Servlet)返回并且其關聯的容器線程完成,我們就根本不意味著請求處理已經完成,因此不應將響應發送回HTTP客戶端。 相反,應將其保留,直到其他執行上下文發出確實已完成請求處理的信號為止,并且它將通過對startAsync調用返回的AsyncContext對象的complete或dispatch方法調用來complete 。
不用說,異步模式與先前存在的Servlet API的有狀態移動部分可能存在幾種交互作用:接下來,我們將對其中的一些進行交互。
異步模式下的錯誤處理
AsyncContext提供了注冊有關請求處理進度和異常情況的偵聽器的功能,但是我們將在容器線程之外運行自管理執行上下文,因此容器無法為我們捕獲和處理異常。
相反, AsyncContext確實提供了一種新的處理委托形式,它將控制權轉移回容器管理的線程,這就是dispatch方法的目的。 通過在請求屬性中設置錯誤條件(和任何其他相關信息)之后使用它,并檢查請求的調度程序類型,我們可以驗證我們確實在處理源自異步處理流程的異常條件,然后選擇重新引發異常,這一次可以依靠容器的管理能力。
這種方法有點復雜,并且基本上需要使用經過改進的前向/調度功能,事實上,這些功能實際上已被過濾器棄用。 但是它可以工作,并且能夠模仿在同步設置中發生的錯誤處理流程。 衡量它的效率當然很有趣。
Servlet API也一直提供sendError功能,但到目前為止, 尚不清楚它是否(以及如何)以異步模式工作,即使在大多數流行的servlet容器(如Jetty和Tomcat)中,這種情況也很容易導致開放問題 。
在異步模式下過濾
Servlet 3.0規范明確禁止在與容器線程不同的執行上下文中運行過濾器鏈接。 此限制意味著,在請求處理鏈末尾的唯一處理程序(即Servlet)可以將請求置于異步模式,而預處理過濾器邏輯只能在容器的線程中執行。
這是非常不幸的,因為自從引入過濾器以來,許多流行的框架和應用程序開發人員就使用了過濾器來執行大量的請求處理,而這些請求處理可以受益于在單獨的執行上下文(例如光纖)中運行而不會阻塞昂貴的容器線程。
實際上,在流行的servlet容器中有一些關于此限制的未解決問題 。
Servlet 3.1:異步HTTP I / O
Servlet 3.0可以從請求處理完成中分離容器的線程和Servlet的處理代碼,但是用于讀取請求和寫入響應的I / O仍然是線程阻塞的。
Servlet 3.1通過setReadListener和setWriteListener方法向請求添加了異步I / O功能 (如果它們已處于異步模式)。
這套新的API有一些缺點:
- 最多可以注冊一個讀和一個寫偵聽器。
- 僅在將請求置于異步模式后 ,API才會強制注冊它們。
- 偵聽器接口是全新的,例如,與NIO API沒有任何共同點。
- 異步API允許更有效的實現,但是這樣做的方式是錯誤的 ,即采用卷積編程模型,而不是提供比線程更有效的執行上下文,同時保持極其有用的“阻塞”抽象。 另一方面, 異步API可以很容易地轉換為高效和富于表現力的光纖阻塞 API 。
“現狀”和前進的方向
許多具有實質性結構的組織已經在基于Servlet的技術上投入了大量資金,因此改變方向是一項相關的成本,需要權衡具體的好處。
他們中的一些人對此感到滿意,并且不受現有缺點的影響。 至于其他的可能性,并希望將來會發生,將來的servlet規范將解決這些問題,但是servlet API是一個龐大而復雜的規范。 它還需要保持一定程度的向后兼容性,因此發布規范審查可能要花費一些時間,更不用說servlet容器正確,有效和可靠地實現它們了。
當然,可以使用諸如Ring之類的servlet替代方案,并且某些組織可以決定使用它們來支付API的費用,以提高生產力并允許構建更多可維護的代碼資產。 對于新的實現而言,此成本通常會更低,而不是移植現有的實現。
如果您的組織中基于servlet的API最明顯的缺點是效率或異步編程模型的缺點,那么Comsat中存在一種非常可行且成本低廉的替代方案:它將使您仍然可以使用簡單的阻塞抽象和熟悉的抽象Servlet API(以及Web和DB領域中的許多其他流行的和標準的API),但具有光纖提供的效率水平。
翻譯自: https://www.javacodegeeks.com/2015/04/on-servlets-and-async-servlets.html
總結
以上是生活随笔為你收集整理的关于Servlet和异步Servlet的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 抗氧化性是什么意思 抗氧化性解释
- 下一篇: Quasar和Akka –比较