异步请求分派器应用程序设计注意事项
异步请求分派器 (ARD) 不是 Servlet 编程的一应俱全解决方案。您必须评估应用程序的需求及 ARD 的使用附加说明。切换所有包含以按异步方式启动,作为解决方案,并不适用于每个方案,但如果明智地使用,那么 ARD 可改善响应时间。
异步请求分派器客户端实现
- 会动态地将 JavaScript 写入响应输出。
- 此 JavaScript 会导致将 Ajax 请求发送回服务器端结果提供程序。
- 由于通道的异步输入/输出 (AIO) 功能,Ajax 请求不会与线程相连;相反,将通过包含回调向 Ajax 请求通知完成。
- 由于浏览器对连接数的限制,客户机一次只对异步包含发出一个请求。
- 原始连接必须在这些包含的生存期内有效。Ajax 请求无法复用原始连接。
- 注释节点,如下所示:
会放在浏览器对象模型中,因为注释节点对页面布局没有影响。<!--uniquePlaceholderID--><!--1-->
- 每当存在完整片段时,可以将响应发送到客户机,且会替换具有同一标识的注释节点。会发出请求,直到检索到所有片段为止。
- 使用客户端聚集时,在所有支持的浏览器上验证应用程序。将使用面向对象的 JavaScript 原则,这样应用程序只需要避免使用方法名 getDynamicDataIBMARD。会在 ARD onload 方法之前启动任何先前指定的 window.onload。
异步请求分派器通道结果服务
对异步 JavaScript 代码中的包含数据所发出的请求会发送到已知的统一资源标识(URI,也称为 URL),ARD 通道可以拦截这些 URI,以阻止经过 Web 容器请求处理。这些 URI 对于每次服务器重新启动都是唯一的。
例如,/IBMARD01234567/asyncInclude.js 是强制对结果进行检索的 JavaScript 的 URI,而 /IBMARD01234567/IBMARDQueryStringEntries?=12000 用来检索标识为 12000 的条目的结果。
为了阻止未经授权的结果访问,会为服务 URI 以及 ARD 条目生成唯一标识。会在会话和 ARD 之间共享公共标识,因此可通过会话配置来配置唯一性。会话标识被视为是安全的,但它们的安全性不如使用轻量级第三方认证 (LTPA) 令牌。
定制客户端聚集
<!--uniquePlaceholderID--><!--x-->
其中,x 是包含的顺序。会从请求属性 com.ibm.websphere.webcontainer.ard.endpointURI 中检索要用来检索结果的端点。<div id="2"><BR>Servlet 3--dispatcher3 requesting Servlet3 to sleep for 0 seconds at: 1187967704265
<BR> Servlet 3--Okay, all done! This should print pop up: third at: 1187967704281 </div>
有关 AsyncRequestDispatcherConfig 和 AsyncRequestDispatcher 接口的其他信息,请复审应用程序编程接口 (API) 文档中的 com.ibm.websphere.webcontainer.async 包。在文档目录中,生成的 API 文档的路径是:
。服务器端聚集
类似于客户端聚集,服务器端聚集将 ARD 通道用作结果服务。ARD 通道知道特定缓冲区集所出现的异步包含。然后,可以在那些缓冲区中搜索包含占位符。由于 JSP 缓存问题,包含占位符可能不在所搜索的缓冲区中。如果发生此问题,那么下一个缓冲区集也必须查找前一个缓冲区集中所缺少的任何包含占位符。在包含返回时,ARD 会尝试反复地聚集,以便可以将响应内容尽可能快地发送到客户机。
并行
工作管理器用于启动包含。如果当前请求的包含数大于工作管理器最大线程池大小,且此大小无法增大,那么工作管理器会启动当前线程上的工作,并跳过占位符写。使用 Concurrency Utilities for Java™ EE,可支持传播原始线程的 Java EE 上下文,其中包括工作区、国际化、应用程序概要信息、z/OS® 操作系统工作负载管理、安全性、事务以及连接上下文。
计时器
将单一计时器用于 ARD,且会为所有超时类型的 ARD 请求创建计时器任务。因为计时器在单个线程上运行,所以向计时器注册的任务可能无法在指定的确切时间运行,因此一项超时操作可能必须等待其他超时操作完成。使用计时器作为最终的手段。
远程请求分派器
可选择性地将 ARD 与远程请求分派器一起使用。通过将请求上下文序列化为 SOAP 消息,以及使用 Web Service 来调用远程服务器,远程请求分派器可以在核心组中的不同应用程序服务器上运行包含。如果在本地发出请求的开销超出通过 Web Service 来创建和发送 SOAP 消息的开销,那么这样做很有用。
异常
如果是所包含 servlet 中的异常,那么 Web 容器会完成映射到异常类型的错误页定义。因此,部署描述符中定义的错误页会显示为聚集页面的一部分。如果包含的行为有所不同,请将逻辑插入到错误页本身。因为包含以异步方式运行,所以顶级 Servlet 不一定仍在使用,因此不会从异步包含(如常规包含)传播回异常。其他包含会完成,以便可以显示部分页面。
如果 ARD 工作管理器耗尽工作程序线程,那么处理此包含的方式类似于处理异步包含。这是缺省设置,但是工作管理器也可以增长,这样就不会导致此状况。在处理期间,用户看不到此处理更改;但是如果启用此更改,那么会在系统日志中将此更改记录为警告消息一次,而在剩余时间,会将此更改记录在跟踪日志中。其他可以触发包含以同步方式发生的状态,将达到时间间隔内最大的到期请求数百分比,且将达到最大的结果存储库大小。
异常可能发生在常规错误页处理作用域以外。例如,工作可能遭工作管理器拒绝。在等待包含响应返回时,计时器可能会到期。ARD 通道充当通用服务来检索结果,可能会接收到无效的标识。在这些情况下,不存在错误页处理的路径,因为缺少让请求正常工作所需的上下文,例如 ServletRequest、ServletResponse 以及 ServletContext。要减轻这些问题所带来的影响,您可以使用 AsyncRequestDispatcherConfig 接口来提供定制错误消息。根据需要提供和国际化缺省值。
异常也可能在设置定制配置所在的请求作用域(例如后续客户端 XMLHttpRequests)外部发生。在这种情况下,必须更改全局配置。这可以通过 com.ibm.wsspi.ard.AsyncRequestDispatcherConfigImpl.getRef() 来检索。
- 包含启动
- 工作管理器会为等待包含启动的时间长度提供超时。由于这通常会立即发生,因此无法通过编程方式来启用。然而,可以在工作管理器设置中对此进行配置。缺省情况下,您不会遇到此超时,因为会在调度工作之前执行最大线程检查。如果对使用中的 AsyncRequestDispatcherConfig 调用 setRetriable(true),那么可以重试工作。
- 包含完成
- 启动超时会在接受工作之后开始计算。可通过控制台或以编程方式通过 AsyncRequestDispatcherConfig.setExecutionTimeoutOverride 方法来配置此超时;缺省值为 60000 ms(一分钟)。会发送来自 AsyncRequestDispatcherConfig.setExecutionTimeoutMessage 的消息,而不是发送包含结果。如果达到此启动超时,但实际包含结果在可以清空数据时才准备就绪,那么会优先使用实际结果。另外,这不适用于 insertFragmentBlocking 调用,这些调用会始终等待包含完成。
- 结果到期
- 由于客户端必须将结果保留在服务中以便为 Ajax 请求发送,因此在客户机关闭且从未检索条目时,需要一种使结果到期的方法。缺省值为一分钟,这对于典型的请求而言是足够的,因为 Ajax 请求会在发送响应之后立即进入。可通过使用 AsyncRequestDispatcherConfig 的 setExpirationTimeoutOverride 方法,以编程方式配置计时器。当某人尝试访问已到期且已从高速缓存中移除的条目时,会显示来自 AsyncRequestDispatcherConfig 的 getOutputRetrievalFailureMessage 方法的消息。在某人请求一个其标识永不存在的结果时,也会向此人发送此消息。
包含或片段
考虑能够以异步方式完成的操作及可以开始操作的时间。理想情况下,在请求的开始进行 getFragment 调用时所有包含已完成,这样包含可以有更多时间来完成,并且如果包含已完成,那么在插入片段时,进行的额外缓存和聚集就较少。然而,调用异步包含更为方便,因为它与常规请求分派器包含遵循相同的模式。
Web 容器
- ServletContext
- 执行跨上下文包含时,充当包含目标的上下文也必须启用 ARD,因为必须已针对 ARD 初始化 Web 应用程序,才能让其 servlet 上下文具有用来检索 AsyncRequestDispatcher 的有效方法。此聚集类型是由原始上下文的配置确定,因为不能混合使用聚集类型。
- ServletRequest
- 必须克隆每个包含的请求。否则,线程之间可能会发生冲突。因为应用程序可以包装缺省请求对象,所以包装器必须实现 com.ibm.wsspi.webcontainer.Servlet.IServletRequest 接口,该接口具有一个方法,即用来创建 CloneNotSupportedException 的 public Object clone 方法。
- 只有在找到用来实现此接口的请求包装器之后,才会执行解包操作。非实现包装器会丢失;然而,为包含配置的 Servlet 过滤器可以对响应进行重新包装。
- 除非在 AsyncRequestDispatcherConfig 上启用 transferState 并调用 insertFragmentBlocking,否则不会将对 ServletRequest 所作的更改传播回顶级 Servlet。
- ServletResponse
- 扩展 com.ibm.websphere.Servlet.response.StoredResponse 的已包装响应由 ARD 创建并发送到包含,因为响应输出必须可以在原始响应生命周期之外进行检索。
- 由于生命周期限制而不支持异步包含中所设置的内部头,除非在 AsyncRequestDispatcher 配置上启用 transferState 并调用 insertFragmentBlocking。同步包含中不支持常规头,如 Servlet 规范所指定。
- 包含过滤器可以对新响应进行重新包装,并且在完成时必须清空。
- ServletInputStream
- 使用 getParameter 来读取参数的应用程序不会造成问题。会在第一个异步包含之前强制解析参数,以阻止对输入流进行并发访问。
- HttpSession
- 必须从顶级 Servlet 执行初始 getSession 调用(会产生 Set-Cookie 头),因为无法预测何时启动包含以及是否已清空头。在 AsyncRequestDispatcherConfig 上启用 transferState 并调用 insertFragmentBlocking 时属于例外情况。在此例外情况下,当您添加头时,通常会抛出异常。
- 过滤器
- 如果包含具有过滤器,那么会对异步线程发出该过滤器。
- 嵌套的异步包含
- 不支持嵌套的异步包含,因为它们会使聚集复杂化。然而,异步包含可以具有嵌套的同步包含。任何执行嵌套异步包含的尝试都会还原为同步包含。
事务
提交至工作管理器的每个任务都使用其自己的事务进行调用,与典型企业 Bean 中容器管理的事务很像。运行时在启动该方法前启动局部事务。如果此事务可以用于调用 Java EE 组件,那么任务可以启动其自己的全局事务。
如果任务创建异常,那么会回滚任何局部事务。如果方法正常返回,那么根据为 Bean 配置的未解析操作策略完成未完成的局部事务。如果任务启动其自己的全局事务,并且未落实此全局事务,那么当方法返回时会回滚该事务。
连接管理
- JNDI 名称在应用程序中硬编码,例如,作为属性或字符串字面值。
- 连接工厂不共享,这是因为没有指定共享作用域的方法。
性能
因为包含以异步方式完成,所以请求的总性能数据必须考虑异步包含的性能。请求的总时间先前可理解为顶级 Servlet 完成的时间,但现在该 Servlet 会在包含完成之前退出。顶级 Servlet 仍占用每个包含所需的大部分额外设置时间。
因此,已将新的 ARD 性能指标添加到性能监视基础结构,以测量完整请求通过 ARD 通道所需的时间。这些指标的详细程度处于请求 URI 级别。
由于 ARD 是必须启用的可选功能部件,因此不使用 RAD 时看不到性能下降。然而,位于已启用 ARD 的应用程序服务器中的非 ARD 应用程序会受到额外层 ARDChannel 的负面影响。通道层不知道它会转至哪个应用程序,因此它对于通道链中的所有应用程序都是开启或关闭的。这些是按虚拟主机进行定义。
安全性
不会根据 Servlet 规范对同步包含分派调用安全性。然而,会通过 Concurrency Utilities for Java EE 来传递安全上下文,以支持以编程方式在 ServletRequest 上使用 isUserInRole 和 getUserPrincipal 方法。还可以使用 Web Service 安全性将此安全上下文传播到远程请求分派。