上周,我与某人谈论了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