Servlet 异步处理

web容器会为每个请求分配一个线程,Servlet3.0新增了异步处理,解决多个线程不释放占据内存的问题。可以先释放容器分配给请求的线程与相关资源,减轻系统负担,原先释放了容器所分配线程的请求,其响应将被延后,可以在处理完成后再对客户端进行响应。

一、AsyncContex简介

为了支持异步处理,在ServletRequest上提供了startAsync()方法。可以通过AsyncContext的getRequest()和getResponse()方法取得请求、响应对象,此次对客户端的响应将暂缓至调用AsyncContext的complete()或dispatch()方法为止。

首先要告知此容器支持Servlet异步处理,如:

  1: @WebServlet(urlPatterns="/some.do", asyncSupported = true)
  2: public class AsyncServlet extends HttpServlet{
  3:
  4: }

例1:异步处理的例子

AsyncServlet.java

  1: package ServletAPI;
  2:
  3: import java.io.IOException;
  4: import java.util.concurrent.ExecutorService;
  5: import java.util.concurrent.Executors;
  6: import javax.servlet.AsyncContext;
  7: import javax.servlet.ServletException;
  8: import javax.servlet.annotation.WebServlet;
  9: import javax.servlet.http.HttpServlet;
 10: import javax.servlet.http.HttpServletRequest;
 11: import javax.servlet.http.HttpServletResponse;
 12:
 13: /**
 14:  * Servlet implementation class AsyncServlet
 15:  */
 16: @WebServlet(name = "AsyncServlet", urlPatterns = { "/async.do" },asyncSupported=true)
 17: public class AsyncServlet extends HttpServlet {
 18:     private static final long serialVersionUID = 1L;
 19:     private ExecutorService executorService=Executors.newFixedThreadPool(10);
 20:     /**
 21:      * @see HttpServlet#HttpServlet()
 22:      */
 23:     public AsyncServlet() {
 24:         super();
 25:         // TODO Auto-generated constructor stub
 26:     }
 27:
 28:     /**
 29:      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
 30:      */
 31:     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 32:       // TODO Auto-generated method stub
 33:       response.setContentType("text/html;charset=UTF-8");
 34:       AsyncContext ctx=request.startAsync();//开始异步处理,释放请求线程
 35:       executorService.submit(new AsynvRequest(ctx)); //创建AsyncRequest,调度线程
 36:
 37:     }
 38:
 39:     /**
 40:      * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
 41:      */
 42:     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 43:       // TODO Auto-generated method stub
 44:     }
 45:     public void destroy(){
 46:       executorService.shutdown();//关闭线程池
 47:     }
 48:
 49: }
 50: 

首先告诉容器,这个Servlet支持异步处理,对于每个请求,Servlet会取得其AsyncContext,并释放容器所分配的线程,响应被延迟后。对于这些被延迟后响应的请求,创建一个实现Runnable接口的AsyncRequest对象,并将其调度一个固定数量的线程池,让这些必须长时间处理的请求,在线程池中完成,不用每次分配线程。

例2:AsyncRequest是个实现Runnable的类,其模拟了长时间处理。

AsyncRequest.java

  1: package ServletAPI;
  2:
  3: import java.io.PrintWriter;
  4: import javax.servlet.AsyncContext;
  5: public class AsynvRequest implements Runnable{
  6:   private AsyncContext ctx;
  7:
  8:   public AsynvRequest(AsyncContext ctx) {
  9:     super();
 10:     this.ctx = ctx;
 11:   }
 12:
 13:   @Override
 14:   public void run() {
 15:     // TODO Auto-generated method stub
 16:     try {
 17:       Thread.sleep(10000);//模拟冗长请求
 18:       PrintWriter out=ctx.getResponse().getWriter();
 19:       out.println("久等了...XD");//输出结果
 20:       ctx.complete();//对客户端完成响应
 21:     } catch (Exception e) {
 22:       // TODO Auto-generated catch block
 23:       throw new RuntimeException(e);
 24:     }
 25:
 26:   }
 27:
 28: }
 29: 

以暂停线程的方式来模拟长时间处理,并输出简单的文字,最后调用complete()对客户端完成响应。

二、模拟服务器推播

HTTP是基于请求、响应模型,如果客户端要获得服务器的最新状态,就必须以定期方式发送请求,查询服务器端的最新状态。

Servlet 3.0提供的异步处理技术,可以解决每个请求占用线程的问题,再结合Ajax异步请求技术,就可以达到类似服务器主动通知浏览器的行为。这就是所谓的服务器端推播。

例3:模拟应用程序不定期产生最新数据,这个部分由实现ServletContextListener的类负责,会在程序启动时进行。

WebInitListener.java

  1: package ServletAPI;
  2:
  3: import java.util.ArrayList;
  4: import java.util.List;
  5: import javax.servlet.AsyncContext;
  6: import javax.servlet.ServletContextEvent;
  7: import javax.servlet.ServletContextListener;
  8: import javax.servlet.annotation.WebListener
  9:
 10: /**
 11:  * Application Lifecycle Listener implementation class WebInitListener
 12:  *
 13:  */
 14: @WebListener
 15: public class WebInitListener implements ServletContextListener {
 16:     private List<AsyncContext> asyncs=new ArrayList<>();//所有的异步请求AsyncContext将存储在这个List中。
 17:
 18:     public void contextDestroyed(ServletContextEvent arg0) {
 19:         // TODO Auto-generated method stub
 20:     }
 21:
 22:   /**
 23:      * @see ServletContextListener#contextInitialized(ServletContextEvent)
 24:      */
 25:     public void contextInitialized(ServletContextEvent arg0) {
 26:         // TODO Auto-generated method stub
 27:       new Thread(new Runnable(){
 28:         public void run(){
 29:           while(true){
 30:             try {//模拟产生随机数字
 31:             Thread.sleep((int)(Math.random()*10000));
 32:             double num=Math.random()*10;
 33:             synchronized (asyncs) {
 34:               for(AsyncContext ctx:asyncs){
 35:                 ctx.getResponse().getWriter().println(num);
 36:                 ctx.complete();
 37:               }
 38:             }
 39:           } catch (Exception e) {
 40:             // TODO Auto-generated catch block
 41:             throw new RuntimeException();
 42:           }
 43:           }
 44:         }
 45:       }).start();
 46:     }
 47:
 48: }
 49: 

有个List会存储所有的异步请求的AsyncContext,并在不定时产生数字后,逐一对客户端响应,并调用AsyncContext的conmplete()来完成请求。

负责接收请求的Servlet,一收到请求,就将之加入到List中。

AsyncNumServlet.java

  1: package ServletAPI;
  2:
  3: import java.io.IOException;
  4: import java.util.List;
  5: import javax.servlet.AsyncContext;
  6: import javax.servlet.ServletException;
  7: import javax.servlet.annotation.WebServlet;
  8: import javax.servlet.http.HttpServlet;
  9: import javax.servlet.http.HttpServletRequest;
 10: import javax.servlet.http.HttpServletResponse;
 11:
 12: @WebServlet(name = "AsyncNumServlet", urlPatterns = { "/asyncNum.do" }, asyncSupported=true)
 13: public class AsyncNumServlet extends HttpServlet {
 14:     private static final long serialVersionUID = 1L;
 15:     private List<AsyncContext> asyncs;
 16:
 17:     public void init() throws ServletException{
 18:       asyncs=(List<AsyncContext>)getServletContext().getAttribute("asyncs");
 19:
 20:     }
 21:     /**
 22:      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
 23:      */
 24:     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 25:       // TODO Auto-generated method stub
 26:       AsyncContext ctx=request.startAsync();//开始异步处理
 27:       synchronized (asyncs) {
 28:         asyncs.add(ctx);//加入维护AsyncContext的List中
 29:       }
 30:     }
 31:
 32: }
 33: 

    由于List是储存为ServletContext属性,所以在Servlet中,必须从ServletContext中取出,每次请求到来时,调用HttpServletRequest的startAsync()进行异步处理,并取得AsyncContext加入维护AsyncContext的List中。

可以使用一个简单的HTML,使用Ajax技术,发送异步请求值服务器端,这个请求会被延迟,直到服务器端完成响应后,更新网页上的资料,并再度发送异步请求:

async.html

  1: <!DOCTYPE html>
  2: <html>
  3: <head>
  4: <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
  5: <title>实时资料</title>
  6: <script>
  7:   function asyncUpdate(){
  8:     var xhr;
  9:     if(window.XMLHttpRequest){
 10:       xhr=new XMLHttpRequest();
 11:     }else
 12:     if(window.ActiveXObject){
 13:       xhr=new ActiveXObject(‘Microsoft.XMLHTTP‘);
 14:     }
 15:     xhr.onreadystatechange=function(){
 16:       if(xhr.readyState==4){
 17:         if(xhr.status==200){
 18:           document.getElementById("data").innerHTML=xhr.responseText;
 19:           asyncUpdate();
 20:         }
 21:       }
 22:     };
 23:     xhr.open(‘GET‘,‘asyncNum.do?timestamp=‘+new Date().getTime());
 24:     xhr.send(null);
 25:   }
 26:   window.onload=asyncUpdate;
 27: </script>
 28: </head>
 29: <body>
 30:   实时资料:<span id="data">0</span>
 31: </body>
 32: </html>

    可以试着用多个浏览器请求这个页面,会看到每个浏览器的资料是同步的。

时间: 2024-08-04 03:40:12

Servlet 异步处理的相关文章

[Servlet3.0]Servlet异步处理

所谓异步处理就是指数据发送方发送数据给数据接收方后,不必等待数据接收方作出响应,可以继续发送下一个数据的通信方式. Servlet 3.0版本提供了相关异步处理的功能,具体实现方式如下: 需要使用注解在对应Servlet配置asyncSupported=true,表示当前Servlet支持异步. 通过Request对象的startAsync(Request, Response)方法获取异步上下文对象. 通过异步上下文对象调用start(new Runnable(){})方法开始异步处理,Runn

springMVC项目异步处理请求的错误Async support must be enabled on a servlet and for all filters involved in async

从github上down下来一个项目,springMVC-chat.作者全是用的注解,也就是零配置. 这可苦了我,经过千辛万苦,最终集成到现在的项目中有一点样子了,结果报出来下面的错误.红色部分.解决方法为,在web.xml中也就是springMVC的总配置文件中加上一句话: <async-supported>true</async-supported> 这句话的位置一定要放正确,否则,一切都是徒劳.至于配置spring异步支持(其实是配置servlet异步支持)的放置位置见下图.

springMVC项目异步错误处理请求Async support must be enabled on a servlet and for all filters involved in async

离github在down下一个项目,springMVC-chat.总体上有标注.这就是零配置. 这可苦了我,费尽周折,最后整合到项目现在看起来有点.出来以下的错误.红色部分.解决方法为,在web.xml中也就是springMVC的总配置文件里加上一句话: <async-supported>true</async-supported> 这句话的位置一定要放正确,否则.一切都是徒劳.至于配置spring异步支持(事实上是配置servlet异步支持)的放置位置见下图. watermark

Java Web基础 --- Servlet 综述(实践篇)

摘要: 伴随 J2EE 6一起发布的Servlet 3.0规范是Servlet规范历史上最重要的变革之一,它的许多新的特性都极大的简化了 Java Web 应用的开发.本文从一个简单的 Servlet 例子开始,说明了如何开发.配置一个 Servlet.此外,还重点叙述了Servlet的一些新特性,包括Servlet 异步处理.Servlet 非阻塞IO 以及 Servlet 文件上传等内容,以便我们对Servlet有一个更全面的了解. 本篇主要介绍 Servlet 实践方面的知识,更多关注于S

[转]Servlet 3.0 新特性详解

原文地址:http://blog.csdn.net/xiazdong/article/details/7208316 Servlet 3.0 新特性概览 1.Servlet.Filter.Listener无需在web.xml中进行配置,可以通过Annotation进行配置: 2.模块化编程,即将各个Servlet模块化,将配置文件也分开配置. 3.Servlet异步处理,应对复杂业务处理: 4.异步Listener,对于异步处理的创建.完成等进行监听: 5. 文件上传API简化: tomcat

Servlet - Upload、Download、Async

Upload.Download.Async 标签 : Java与Web Upload-上传 随着3.0版本的发布,文件上传终于成为Servlet规范的一项内置特性,不再依赖于像Commons FileUpload之类组件,因此在服务端进行文件上传编程变得不费吹灰之力. 客户端 要上传文件, 必须利用multipart/form-data设置HTML表单的enctype属性,且method必须为POST: <form action="simple_file_upload_servlet.do

java Servlet 3.0新特性例子

迟到的作业,Servlet3.0 已经出来很久了也没去关注,一直守旧于老的技术,今天抽时间学习了一下补个作业. Servlet3.0  特性: 1.Servlet.Filter.Listener无需在web.xml中进行配置,可以通过Annotation进行配置: 2.模块化编程,即将各个Servlet模块化,将配置文件也分开配置. 3.Servlet异步处理,应对复杂业务处理: 4.异步Listener,对于异步处理的创建.完成等进行监听: 5. 文件上传API简化: 备注:tomcat7.0

Java Web基础 --- Servlet 综述(理论篇)

摘要: Web 技术成为当今主流的互联网 Web 应用技术之一,而 Servlet 是 Java Web 技术的核心基础.本文首先从请求/响应架构应用的大背景谈起 Servlet 的由来,明确 Servlet 的产生动机,并揭示了 Servlet 的本质以及其在标准MVC模式中所扮演的角色.紧接着,给出了 Servlet族的继承结构,并对族内的接口和抽象类作了进一步的介绍,并给出开发一个Servlet程序的常用做法.在此基础上,我们图文并茂地介绍了 Servlet 的生命周期与执行流程,清晰展现

异步化,高并发大杀器

聊聊如何让项目异步化的一些事. 1.同步和异步,阻塞和非阻塞 同步和异步,阻塞和非阻塞, 这个几个词已经是老生常谈,当时常常还是有很多同学分不清楚,以为同步肯定就是阻塞,异步肯定就是非阻塞,其他他们不是一回事. 同步和异步关注的是结果消息的通信机制 同步:同步的意思就是调用方需要主动等待结果的返回异步:异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态通知,回调函数等.阻塞和非阻塞主要关注的是等待结果返回调用方的状态 阻塞:是指结果返回之前,当前线程被挂起,不做任何事非阻塞:是指