异步 Servlet 最佳实践
异步 servlet 功能使您可以处理传入请求和响应,而无需绑定到发起请求的原始线程。
使用异步 servlet 时考虑以下最佳实践:
- 应用程序不应为每个需要的异步操作衍生新线程。至少,应用程序应该使用线程池或使用 AsyncContext start(Runnable) 方法。
- 在客户机/浏览器端,您可以使用 AJAX 以异步方式更新页面的某些部分。
- Servlet 容器确保要完成或分派的调用不会启动,直到存在发出 startAsync 命令的 Web 容器线程。但是,servlet 容器不会同时使用同一请求和响应处理多个线程。在此情况下,应用程序可以处理它自己的并行或同步问题,但由于容易发生死锁或竞争状态,因此,不建议执行此操作。如果 dispatch 或 complete 方法是从客户创建的线程或使用 start(Runnable) 启动的可运行程序调用的,那么可在新线程上立即启动 dispatch 或 complete 方法,对启动这些调用的线程的请求或响应进行任何进一步修改都是危险的。两个线程都将有权访问请求和响应,如果两个线程都在修改这些对象,那么这会产生不确定的结果。因此,在从调用分派的相同线程执行分派后,不要对请求或响应调用任何方法。 在调用操作完成后,不要对请求或响应调用任何方法。
- 异步侦听器具有 onTimeout 方法,此方法在达到异步操作的时间限制时会启动。然而,onTimeout 在其他线程上运行时,异步操作可能仍在一个线程上运行。这种情况是最常见的方式,有多个线程意外同时使用相同的请求和响应。在此场景中,有一个简单方法是使用来自 AsyncListener 和异步操作的共享 AtomicBoolean 方法,如下所示:
借助此方法,只有一个线程可以获取写入响应的访问权。AtomicBoolean isOkayToRun = (AtomicBoolean) request.getAttribute("isOkayToRun"); if (isOkayToRun.setAndGet(false)){ //do a dispatch }
- 达到超时后,Web 容器会尝试取消按 start(Runnable) 方法的调用排队的任何可运行程序。 但是,已启动的可运行程序不可中断,因为中断会导致内存泄露。
- 执行超时通知的线程数量很少。如果客户机的连接较慢,那么即使是较小的写入操作也需要一定时间,因此,不建议在超时后尝试任何密集的操作或任何写入操作。当您禁用异步超时时,更容易遇到 OutOfMemory 错误或耗尽 TCP 通道连接数。缺省超时为 30 秒。
- 您可以在管理控制台中通过单击 ,来配置诸如超时设置和 AsyncContext start(Runnable) 方法的一些异步 servlet 选项。请参阅“Web 容器设置”主题以了解有关配置 Web 容器的信息。
要点: 使用异步 servlet 时,不支持异步请求分派器 (ARD) 和远程请求分派器 (RRD)。
提示: 查看 Web 应用程序计数器主题以了解异步 servlet 的度量。