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