• 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>

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

  • 相关阅读:
    初始化操作由“case”标签跳过
    NI采集卡DO例程2 WriteDigChanExtClk
    QT中QString 类的使用获取指定字符位置、截取子字符串等
    NI采集卡DO例程1 WriteDigChan
    springboot链路追踪ID
    Java读取pem文件使用RSA加密解密
    docker安装logstash并读取syslog数据
    MySQL事务隔离级别
    Golang的RSA加密
    springboot整合log4j
  • 原文地址:https://www.cnblogs.com/liuzhongfeng/p/6107594.html
Copyright © 2020-2023  润新知