servlet3异步 例子

Async servlet was introduced in Servlet 3. It’s a great way to deal with thread starvation problem with long running threads.

Servlet 3中引入了异步Servlet。这是处理长时间运行的线程的线程匮乏问题的好方法。

异步Servlet (Async Servlet)

Before we jump into understanding what Async Servlet is, let’s try to understand why do we need it.

在深入了解什么是异步Servlet之前,让我们尝试了解为什么需要它。

Let’s say we have a Servlet that takes a lot of time to process, something like below.

假设我们有一个Servlet,处理时间很长,如下所示。

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();}}}

If we hit above servlet through browser with URL as https://localhost:8080/AsyncServletExample/LongRunningServlet?time=8000, we get response as “Processing done for 8000 milliseconds!!” after 8 seconds. Now if you will look into server logs, you will get following log:

如果我们通过URL为https://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.

So our Servlet Thread was running for ~8+ seconds, although most of the processing has nothing to do with the servlet request or response.

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

This can lead to Thread Starvation – since our servlet thread is blocked until all the processing is done. If server gets a lot of requests to process, it will hit the maximum servlet thread limit and further requests will get Connection Refused errors.

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

Prior to Servlet 3.0, there were container specific solution for these long running threads where we can spawn a separate worker thread to do the heavy task and then return the response to client. The servlet thread returns to the servlet pool after starting the worker thread. Tomcat’s Comet, WebLogic’s FutureResponseServlet and WebSphere’s Asynchronous Request Dispatcher are some of the example of implementation of asynchronous processing.

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

The problem with container specific solution is that we can’t move to other servlet container without changing our application code, that’s why Async Servlet support was added in Servlet 3.0 to provide standard way for asynchronous processing in servlets.

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

异步Servlet实现 (Asynchronous Servlet Implementation)

Let’s see steps to implement async servlet and then we will provide async supported servlet for above example.

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

  1. First of all the servlet where we want to provide async support should have @WebServlet annotation with asyncSupported value as true.首先,我们要提供异步支持的servlet应该具有@WebServlet 批注 ,其asyncSupported值为true
  2. Since the actual work is to be delegated to another thread, we should have a thread pool implementation. We can create thread pool using Executors framework and use servlet context listener to initiate the thread pool.由于实际工作将委托给另一个线程,因此我们应该有一个线程池实现。 我们可以使用Executors框架创建线程池,并使用servlet上下文侦听器来启动线程池。
  3. We need to get instance of AsyncContext through ServletRequest.startAsync() method. AsyncContext provides methods to get the ServletRequest and ServletResponse object references. It also provides method to forward the request to another resource using dispatch() method.我们需要通过ServletRequest.startAsync()方法获取AsyncContext的实例。 AsyncContext提供了获取ServletRequest和ServletResponse对象引用的方法。 它还提供了使用dispatch()方法将请求转发到另一个资源的方法。
  4. We should have a Runnable implementation where we will do the heavy processing and then use AsyncContext object to either dispatch the request to another resource or write response using ServletResponse object. Once the processing is finished, we should call AsyncContext.complete() method to let container know that async processing is finished.我们应该有一个Runnable实现 ,在这里我们将进行繁重的处理,然后使用AsyncContext对象将请求分派到另一个资源,或者使用ServletResponse对象写入响应。 处理完成后,我们应调用AsyncContext.complete()方法以使容器知道异步处理已完成。
  5. We can add AsyncListener implementation to the AsyncContext object to implement callback methods – we can use this to provide error response to client incase of error or timeout while async thread processing. We can also do some cleanup activity here.我们可以将AsyncListener实现添加到AsyncContext对象中以实现回调方法–我们可以使用它为异步线程处理过程中发生错误或超时的情况提供对客户端的错误响应。 我们还可以在此处进行一些清理活动。

Once we will complete our project for Async servlet Example, it will look like below image.

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

在Servlet上下文侦听器中初始化辅助线程池 (Initializing Worker Thread Pool in Servlet Context Listener)

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();}}

The implementation is pretty straight forward, if you are not familiar with Executors framework, please read Thread Pool Executor.

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

For more details about listeners, please go through Servlet Listener Example.

有关侦听器的更多详细信息,请浏览Servlet Listener Example 。

工作线程实现 (Worker Thread Implementation)

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();}}
}

Notice the use of AsyncContext and it’s usage in getting request and response objects and then completing the async processing with complete() method call.

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

AsyncListener实现 (AsyncListener Implementation)

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");}}

Notice the implementation of onTimeout() method where we are sending timeout response to client.

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

异步Servlet示例实现 (Async Servlet Example implementation)

Here is the implementation of our async servlet, notice the use of AsyncContext and ThreadPoolExecutor for processing.

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

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 Web应用程序 (Run Async Servlet web application)

Now when we will run above servlet with URL as https://localhost:8080/AsyncServletExample/AsyncLongRunningServlet?time=8000 we get the same response and logs as:

现在,当我们在servlet上以URL https://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

If we run with time as 9999, timeout occurs and we get response at client side as “TimeOut Error in Processing” and in logs:

如果我们将时间设置为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)

Notice that servlet thread finished execution quickly and all the major processing work is happening in other thread.

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

Thats all for Async Servlet, I hope you liked it.

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

Download Servlet 3 Async Example Project下载Servlet 3异步示例项目

翻译自: https://www.journaldev.com/2008/async-servlet-example

servlet3异步 例子

servlet3异步 例子_异步Servlet示例相关推荐

  1. 同步等待 异步等待_异步/等待与承诺互操作性

    同步等待 异步等待 Usually, when discussing Promises and async/await syntax, people frame it as an "eith ...

  2. python 异步数据库_异步Python和数据库

    python 异步数据库 The asynchronous programming topic is difficult to cover. These days, it's not just abo ...

  3. 同步等待 异步等待_异步/等待和承诺的解释

    同步等待 异步等待 The async / await operators make it easier to implement many async Promises. They also all ...

  4. java jpa 异步编程_异步处理时的JPA

    java jpa 异步编程 几年前,在Java世界中,几乎显而易见的是,每个"企业"类项目都需要JPA与数据库进行通信. JPA是Joel Spolsky描述的" 泄漏抽 ...

  5. 异步重试_异步重试模式

    异步重试 当您有一段经常失败且必须重试的代码时,此Java 7/8库提供了丰富且简洁的API以及针对此问题的快速且可扩展的解决方案: ScheduledExecutorService schedule ...

  6. 异步生成器_异步生成器作为状态管理的替代方法

    异步生成器 Async Generators is a simple but powerful feature that is now a part of JavaScript. It unlocks ...

  7. nodejs异步测试_异步过程的自动化测试

    nodejs异步测试 自从我开发了具有异步行为的服务器端应用程序以来已经有一段时间了,而该行为还不是事件驱动的系统. 异步行为始终是设计和测试中一个有趣的挑战. 通常,异步行为不应该很难进行单元测试– ...

  8. flask-mail异步发送邮件_异步、定时、邮件任务

    十.异步.定时.邮件任务 1.异步任务 1.创建一个service包 2.创建一个类AsyncService 异步处理还是非常常用的,比如我们在网站上发送邮件,后台会去发送邮件,此时前台会造成响应不动 ...

  9. java合成聚和例子_合成聚合原则(示例代码)

    1.合成聚合原则定义 又叫做合成复用原则(Composite ReusePrinciple或CRP),尽量使用对象组合,而不是继承来达到复用的目的.就是在一个新的对象里面使用一些已有的对象,使之成为新 ...

最新文章

  1. 5G小科普(漫画版,So easy!)
  2. [HDU 4666]Hyperspace[最远曼哈顿距离][STL]
  3. 学习10:Python重要知识
  4. 及cp含义_新媒体运营炒CP,既好用,又好玩(第327回)
  5. 14.6.4 Configuring the Memory Allocator for InnoDB 配置InnoDB 内存分配器
  6. Spring AOP之概念解释
  7. 给tftp服务器传文件,袖针文件传输服务器Tftpd32
  8. Tomcat 配置文件详解
  9. C++ QQ游戏 连连看外挂 内存挂入门
  10. 改变文本颜色和字体大小的脚本
  11. 谈谈你对Spring 的理解
  12. 腾讯副总裁曾宇:谈谈腾讯的技术价值观与技术人才修炼
  13. 安装Redis使用make命令出现make /bin/sh: cc: 未找到命令 make[1]: 离开目录“/usr/local/mytools/redis-5.0.0/src“
  14. facebook聊单?SaleSmatly来助力
  15. 网络世界强权崛起,全球竞相取经
  16. 自动生成无课表(云南农业大学)
  17. 【Android】为啥子线程抛出异常主线程会崩溃?UncaughtExceptionHandler
  18. var,let和const
  19. Android博客大汇总
  20. 以26个英文字母开头的动物名字

热门文章

  1. dspic flash不够后,选择优化等级
  2. Android布局之RelativeLayout
  3. WPF采用MVVM模式(绑定:纯前台、命令:触发器绑定命令)
  4. SpringAOP和AspectJ
  5. linux something
  6. POJ 3621:Sightseeing Cows(最优比率环)
  7. utuntu 视频 无声
  8. Myeclipse中添加XFire插件支持
  9. Blocks in Objective-C
  10. JavaScript正则表达式使用详解