servlet3异步

在深入了解什么是异步Servlet之前,让我们尝试了解为什么需要它。 假设我们有一个Servlet,处理时间很长,如下所示。

LongRunningServlet.java

package com.journaldev.servlet;import java.io.IOException;
import java.io.PrintWriter;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet("/LongRunningServlet")
public class LongRunningServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {long startTime = System.currentTimeMillis();System.out.println("LongRunningServlet Start::Name="+ Thread.currentThread().getName() + "::ID="+ Thread.currentThread().getId());String time = request.getParameter("time");int secs = Integer.valueOf(time);// max 10 secondsif (secs > 10000)secs = 10000;longProcessing(secs);PrintWriter out = response.getWriter();long endTime = System.currentTimeMillis();out.write("Processing done for " + secs + " milliseconds!!");System.out.println("LongRunningServlet Start::Name="+ Thread.currentThread().getName() + "::ID="+ Thread.currentThread().getId() + "::Time Taken="+ (endTime - startTime) + " ms.");}private void longProcessing(int secs) {// wait for given time before finishingtry {Thread.sleep(secs);} catch (InterruptedException e) {e.printStackTrace();}}}

如果我们通过浏览器在URL上方为http://localhost:8080/AsyncServletExample/LongRunningServlet?time=8000 servlet上方收到响应,则响应为“处理完成8000毫秒!” 8秒后。 现在,如果您查看服务器日志,将得到以下日志:

LongRunningServlet Start::Name=http-bio-8080-exec-34::ID=103
LongRunningServlet Start::Name=http-bio-8080-exec-34::ID=103::Time Taken=8002 ms.

因此,尽管大多数处理与Servlet请求或响应无关,但我们的Servlet线程运行了大约8+秒。

这可能导致线程饥饿 -由于在所有处理完成之前我们的servlet线程被阻塞,因此如果服务器收到大量要处理的请求,它将达到最大servlet线程限制,并且进一步的请求将出现Connection Refused错误。

在Servlet 3.0之前,有针对这些长时间运行的线程的特定于容器的解决方案,我们可以生成单独的工作线程来执行繁重的任务,然后将响应返回给客户端。 启动工作线程后,Servlet线程返回到Servlet池。 Tomcat的Comet,WebLogic的FutureResponseServlet和WebSphere的异步请求分派器是异步处理实现的一些示例。

特定于容器的解决方案的问题在于,在不更改应用程序代码的情况下,我们无法移至其他servlet容器,这就是为什么在Servlet 3.0中添加了Async Servlet支持以为Servlet中的异步处理提供标准方式的原因。

异步Servlet实现

让我们看一下实现异步servlet的步骤,然后为上述示例提供异步支持的servlet。

  1. 首先,我们要提供异步支持的servlet应该具有@WebServlet 批注 ,其asyncSupported值为true
  2. 由于实际工作将委托给另一个线程,因此我们应该有一个线程池实现。 我们可以使用Executors框架创建线程池,并使用servlet上下文侦听器来启动线程池。
  3. 我们需要通过ServletRequest.startAsync()方法获取AsyncContext的实例。 AsyncContext提供了获取ServletRequest和ServletResponse对象引用的方法。 它还提供了使用dispatch()方法将请求转发到另一个资源的方法。
  4. 我们应该有一个Runnable实现 ,在这里我们将进行繁重的处理,然后使用AsyncContext对象将请求分派到另一个资源,或者使用ServletResponse对象写入响应。 处理完成后,我们应调用AsyncContext.complete()方法以使容器知道异步处理已完成。
  5. 我们可以将AsyncListener实现添加到AsyncContext对象中以实现回调方法–我们可以使用它为异步线程处理过程中发生错误或超时的情况提供对客户端的错误响应。 我们还可以在此处进行一些清理活动。

一旦我们完成了用于异步Servlet示例的项目,它将如下图所示。

在Servlet上下文侦听器中初始化辅助线程池

AppContextListener.java

package com.journaldev.servlet.async;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;@WebListener
public class AppContextListener implements ServletContextListener {public void contextInitialized(ServletContextEvent servletContextEvent) {// create the thread poolThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L,TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100));servletContextEvent.getServletContext().setAttribute("executor",executor);}public void contextDestroyed(ServletContextEvent servletContextEvent) {ThreadPoolExecutor executor = (ThreadPoolExecutor) servletContextEvent.getServletContext().getAttribute("executor");executor.shutdown();}}

该实现非常简单,如果您不熟悉Executors框架,请阅读Thread Pool Executor

有关监听器的更多详细信息,请阅读Servlet Listener Tutorial

工作线程实现

AsyncRequestProcessor.java

package com.journaldev.servlet.async;import java.io.IOException;
import java.io.PrintWriter;import javax.servlet.AsyncContext;public class AsyncRequestProcessor implements Runnable {private AsyncContext asyncContext;private int secs;public AsyncRequestProcessor() {}public AsyncRequestProcessor(AsyncContext asyncCtx, int secs) {this.asyncContext = asyncCtx;this.secs = secs;}@Overridepublic void run() {System.out.println("Async Supported? "+ asyncContext.getRequest().isAsyncSupported());longProcessing(secs);try {PrintWriter out = asyncContext.getResponse().getWriter();out.write("Processing done for " + secs + " milliseconds!!");} catch (IOException e) {e.printStackTrace();}//complete the processingasyncContext.complete();}private void longProcessing(int secs) {// wait for given time before finishingtry {Thread.sleep(secs);} catch (InterruptedException e) {e.printStackTrace();}}
}

请注意,AsyncContext的用法及其在获取请求和响应对象,然后通过complete()方法调用完成异步处理的用法。

AsyncListener实现

AppAsyncListener.java

package com.journaldev.servlet.async;import java.io.IOException;
import java.io.PrintWriter;import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebListener;@WebListener
public class AppAsyncListener implements AsyncListener {@Overridepublic void onComplete(AsyncEvent asyncEvent) throws IOException {System.out.println("AppAsyncListener onComplete");// we can do resource cleanup activity here}@Overridepublic void onError(AsyncEvent asyncEvent) throws IOException {System.out.println("AppAsyncListener onError");//we can return error response to client}@Overridepublic void onStartAsync(AsyncEvent asyncEvent) throws IOException {System.out.println("AppAsyncListener onStartAsync");//we can log the event here}@Overridepublic void onTimeout(AsyncEvent asyncEvent) throws IOException {System.out.println("AppAsyncListener onTimeout");//we can send appropriate response to clientServletResponse response = asyncEvent.getAsyncContext().getResponse();PrintWriter out = response.getWriter();out.write("TimeOut Error in Processing");}}

注意onTimeout()方法的实现,在该方法中,我们向客户端发送超时响应。

这是我们异步servlet的实现,请注意使用AsyncContext和ThreadPoolExecutor进行处理。

AsyncLongRunningServlet.java

package com.journaldev.servlet.async;import java.io.IOException;
import java.util.concurrent.ThreadPoolExecutor;import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet(urlPatterns = "/AsyncLongRunningServlet", asyncSupported = true)
public class AsyncLongRunningServlet extends HttpServlet {private static final long serialVersionUID = 1L;protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {long startTime = System.currentTimeMillis();System.out.println("AsyncLongRunningServlet Start::Name="+ Thread.currentThread().getName() + "::ID="+ Thread.currentThread().getId());request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);String time = request.getParameter("time");int secs = Integer.valueOf(time);// max 10 secondsif (secs > 10000)secs = 10000;AsyncContext asyncCtx = request.startAsync();asyncCtx.addListener(new AppAsyncListener());asyncCtx.setTimeout(9000);ThreadPoolExecutor executor = (ThreadPoolExecutor) request.getServletContext().getAttribute("executor");executor.execute(new AsyncRequestProcessor(asyncCtx, secs));long endTime = System.currentTimeMillis();System.out.println("AsyncLongRunningServlet End::Name="+ Thread.currentThread().getName() + "::ID="+ Thread.currentThread().getId() + "::Time Taken="+ (endTime - startTime) + " ms.");}}

运行异步Servlet

现在,当我们在servlet上运行,URL为http://localhost:8080/AsyncServletExample/AsyncLongRunningServlet?time=8000我们得到的响应和日志如下:

AsyncLongRunningServlet Start::Name=http-bio-8080-exec-50::ID=124
AsyncLongRunningServlet End::Name=http-bio-8080-exec-50::ID=124::Time Taken=1 ms.
Async Supported? true
AppAsyncListener onComplete

如果我们将时间设置为9999,则会发生超时,并在客户端以“处理中的超时错误”和日志形式获得响应:

AsyncLongRunningServlet Start::Name=http-bio-8080-exec-44::ID=117
AsyncLongRunningServlet End::Name=http-bio-8080-exec-44::ID=117::Time Taken=1 ms.
Async Supported? true
AppAsyncListener onTimeout
AppAsyncListener onError
AppAsyncListener onComplete
Exception in thread "pool-5-thread-6" java.lang.IllegalStateException: The request associated with the AsyncContext has already completed processing.at org.apache.catalina.core.AsyncContextImpl.check(AsyncContextImpl.java:439)at org.apache.catalina.core.AsyncContextImpl.getResponse(AsyncContextImpl.java:197)at com.journaldev.servlet.async.AsyncRequestProcessor.run(AsyncRequestProcessor.java:27)at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)at java.lang.Thread.run(Thread.java:680)

注意,servlet线程快速完成了执行,所有主要的处理工作都在其他线程中进行。

这就是异步Servlet的全部,希望您喜欢它。

  • 下载AsyncServletExample项目
参考: 开发者食谱博客上来自JCG合作伙伴 Pankaj Kumar 的Servlet 3异步Servlet功能 。

翻译自: https://www.javacodegeeks.com/2013/08/async-servlet-feature-of-servlet-3.html

servlet3异步

servlet3异步_Servlet 3的异步Servlet功能相关推荐

  1. servlet 3.0异步_Servlet 3.0异步处理可将服务器吞吐量提高十倍

    servlet 3.0异步 Servlet是Java中处理服务器端逻辑的主要组件,新的3.0规范引入了一些非常有趣的功能,其中异步处理是最重要的功能之一. 可以利用异步处理来开发高度可伸缩的Web应用 ...

  2. Servlet 3的异步Servlet功能

    在深入了解什么是异步Servlet之前,让我们尝试了解为什么需要它. 假设我们有一个Servlet,处理时间很长,如下所示. LongRunningServlet.java package com.j ...

  3. python异步编程视频_asyncio异步编程【含视频教程】

    Python Python开发 Python语言 asyncio异步编程[含视频教程] 不知道你是否发现,身边聊异步的人越来越多了,比如:FastAPI.Tornado.Sanic.Django 3. ...

  4. 【转】1.6异步编程:IAsyncResult异步编程模型 (APM)

    传送门:异步编程系列目录-- 大部分开发人员,在开发多线程应用程序时,都是使用ThreadPool的QueueUserWorkItem方法来发起一次简单的异步操作.然而,这个技术存在许多限制.最大的问 ...

  5. java 日志 异步_log4j 详解异步日志的配置和测试

    log4j 详解异步日志的配置和测试 日志可以帮助我们分析故障原因,做些数据挖掘的工作.最简单的日志方法,就是自己写个写文件的方法,在需要打日志的时候调用下,但是这显然不可能在实际工程上用.还有个问题 ...

  6. springboot异步和切面_Spring异步编程 你的@Async就真的异步吗?异步历险奇遇记

    引言有点长 前端的宝宝会用ajax,用异步编程到快乐的不行~ 我们java也有异步,用起来比他们还快乐~ 我们biaji一个注(gǒupí)解(gāoyào),也是快乐风男... 且看下面的栗子: 注 ...

  7. springboot异步和切面_Spring异步编程 | 你的@Async就真的异步吗 ☞ 异步历险奇遇记...

    引言有点长 前端的宝宝会用ajax,用异步编程到快乐的不行~ 我们java也有异步,用起来比他们还快乐~ 我们bia~ji~一个注(gǒupí)解(gāoyào),也是快乐风男... 且看下面的栗子: ...

  8. springboot异步和切面_Spring异步编程 | 你的@Async就真的异步吗?异步历险奇遇记

    Spring异步编程 | 你的@Async就真的异步吗?异步历险奇遇记 点击上方"java进阶架构师",选择右上角"置顶公众号" 20大进阶架构专题每日送达 引 ...

  9. Python异步: 什么时候使用异步?(3)

    从广义上讲,Asyncio 是新的.流行的.讨论广泛的和令人兴奋的.然而,对于何时应该在项目中采用它存在很多困惑. 我们什么时候应该在 Python 中使用 asyncio? 1. 在 Python ...

最新文章

  1. LeetCode简单题之最长特殊序列 Ⅰ
  2. unittest安装教程_unittest框架与自动化测试环境的搭建
  3. Javascript 是如何体现继承的 ?
  4. java int parse_java中Integer.parseInt和Integer.valueOf的区别
  5. 生命周期结束,Spring Boot 1.x退役
  6. python多线程没有java_Java 多线程启动为什么调用 start() 方法而不是 run() 方法?...
  7. Socket相关操作超时
  8. java 绘制sin函数图像_MATLAB基础学习之三维曲线的绘制
  9. 支付宝上线“老年版相互宝”:爸妈终于可以加入了
  10. (cljs/run-at (JSVM. :all) 一次说白DataType、Record和Protocol) 1
  11. 农民工兄弟学C#(4)
  12. java留言功能_java web实现简单留言板功能
  13. Shopee运营中如何规避账号安全风险?站斧超级浏览器防关联运营
  14. IAR Fatal error
  15. 苹果Mac延时摄影视频制作工具:Persecond
  16. php imap 库_php imap_open 实例教程
  17. 【入门】【递推】走楼梯
  18. 小程序实现图片放大预览功能
  19. 只管去做:让你迅速实现增值的目标管理法
  20. 6、react 模块和组件

热门文章

  1. MySQL Replace()函数
  2. 浅谈流处理算法 (1) – 蓄水池采样
  3. Node.JS第二讲笔记
  4. ssh(Spring+Spring mvc+hibernate)简单增删改查案例
  5. JSP 登录案例实现
  6. 解决获取请求参数的乱码问题
  7. JavaScript(笔记)
  8. android之微信分享文本
  9. import javax.servlet.ServletRequest 关于IDEA javax.servlet.http.HttpServletRequest 不存在 解决方案
  10. Linux程序之触摸,linux 触摸屏驱动编写