上周,我與某人談論了Grails 2中對Servlet 3.0異步功能的新支持,并意識到我對可用功能并不了解。 所以我想我會嘗試一下并分享一些例子。 該文檔對這個主題有些了解,因此首先介紹一些背景信息。
在3.0規范中進行異步工作的主要方式是
javax.servlet.ServletRequest
類中的新startAsync
方法。 這將返回javax.servlet.AsyncContext
接口的實例,該實例具有生命周期方法(例如dispatch
和complete
,為您提供了對請求和響應的掛鉤,并允許您注冊javax.servlet.AsyncListener
。 您調用傳入Runnable
的start
方法來執行異步工作。 使用這種方法可以釋放服務器資源而不是進行阻塞,這可以提高可伸縮性,因為您可以處理更多的并發請求。 為了使用此功能,處理請求的servlet必須支持異步,并且過濾器鏈中所有應用的過濾器也必須支持。 主Grails Servlet( GrailsDispatcherServlet )在web.xml模板的3.0版本中注冊,并且
async-supported
屬性設置為true。 Servlet3AsyncWebXmlProcessor生成后,將<async-supported>true</async-supported>
到web.xml中的所有過濾器聲明中。 這樣就為您覆蓋了; 您沒有必需的web.xml配置。 您還必須配置為使用Servlet API 3.0。 這很容易做到; 只需將
grails.servlet.version
的值grails.servlet.version
為“ 3.0? 默認值“ 2.5”。 請注意,application.properties中有一個舊設置,名稱為app.servlet.version
; 您應該從application.properties文件中刪除此行,因為它的值在運行時會被BuildConfig.groovy中的值忽略并覆蓋。 但是,您不會在控制器的請求上調用
startAsync
; 直接在控制器上調用startAsync
。 此方法是作為控制器方法添加的(作為Controller的AST轉換的一部分,從ControllersAsyncApi連接 (如果您感到好奇,可以通過ControllerAsyncTransformer連接 ))。 調用控制器的startAsync
方法非常重要,因為它可以執行所有標準工作,而且還可以添加Grails集成。 這包括添加邏輯以集成所有已注冊的PersistenceContextInterceptor實例,例如將Hibernate Session綁定到線程,完成后刷新等,并與Sitemesh集成。 這是通過返回的實例來實現的 GrailsAsyncContext為其余部分添加額外的行為并委托給容器提供的實際實例(例如Tomcat中的
org.apache.catalina.core.AsyncContextImpl
)。 請求中還有其他一些與異步相關的新方法。 它們包括
boolean isAsyncStarted()
和AsyncContext getAsyncContext()
。 我已經附加了一個示例應用程序(請參閱下面的鏈接)以演示這些功能。 有兩個部分: 一個異步查詢股票價格的簡單控制器,以及一個聊天應用程序。
StockController
非常簡單。 它只有一個動作,因此會暫停以查詢所請求的股票報價器的當前股價。 它異步執行此操作,但通常速度非常快,因此您可能看不到與串行方法的真正區別。 但是,這種模式可以推廣到執行更多耗時的任務。 調用http:// localhost:8080 / asynctest / stock / GOOG,http:// localhost:8080 / asynctest / stock / AAPL,http:// localhost:8080 / asynctest / stock / VMW等進行測試。
第二個示例涉及更多,并基于Java EE 6 SDK中的“ async-request-war”示例。 這實現了一個聊天應用程序(它以前是通過Comet實現的)。 SDK的示例是一個大servlet。 我將其拆分為一個控制器以執行標準請求工作,并將其
ChatManager
為ChatManager
類(在resources.groovy中注冊為Spring Bean)來處理客戶端注冊,消息排隊和調度以及相關的錯誤處理。 該實現使用隱藏的iframe來啟動長時間運行的請求。 它永遠不會完成,并且用于將消息發送回每個注冊的客戶端。 當您“登錄”或發送消息時,控制器將處理請求并將響應消息排隊。 然后,
ChatManager
循環遍歷每個已注冊的AsyncContext
,并將JSONP發送到iframe,該iframe使用傳入消息更新主頁中的文本區域。 使我困擾了很長時間的一件事是,該示例在SDK示例中運行良好,但在我的示例中卻無法運行。 一切看起來不錯,但iframe并未收到消息。 事實證明,這是由于進行了適當的優化以使響應呈現盡可能快。 不幸的是,這導致響應編寫器上的
flush()
調用被忽略。 由于我們需要響應式更新,并且不會呈現較大的html頁面,因此我添加了代碼來查找由Grails代碼包裝的真實響應,并直接發送給它。 在兩個瀏覽器中打開http:// localhost:8080 / asynctest /嘗試一下。 一旦您“登錄”到兩者,發送的消息將在兩個瀏覽器中顯示。
有關測試應用程序的一些注意事項:
- 所有客戶端邏輯都在web-app / js / chat.js中
- grails-app / views / chat / index.gsp是主頁; 它創建了文本區域來顯示消息,而隱藏的iframe保持連接狀態并收聽消息
- 這需要實現3.0規范的Servlet容器。 由tomcat插件提供并由run-app使用的Tomcat版本,而所有7.x版本的Tomcat都有。
- 我運行
install-templates
并編輯了web.xml以添加metadata-complete="true"
以防止Tomcat掃描所有jar文件中的帶注釋的類–由于版本7.0.26中已修復的錯誤(當前未發布,因此這可能導致OOME)) - 由于聊天部分基于舊代碼,因此它使用Prototype,但可以輕松使用jQuery。
您可以在此處下載示例應用程序代碼。
參考: An Solipsists博客上的JCG合作伙伴 Burt Beckwith 提供的在Grails 2.0中使用Servlet 3.0異步功能 。
翻譯自: https://www.javacodegeeks.com/2012/06/using-servlet-30-async-features-in.html