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