第十三章_异步处理

简介:

13.1、概述

计算机的内存是有限的。Servlet/JSP容器的设计者很清楚这一点,因此他们提供了一些可以进行配置的设置,以确保容器能够在宿主机器中正常运行。例如,在Tomcat7中,处理进来请求的最多线程数量为200。如果是多处理器的服务器,则可以放心地增加线程数量,不过建议你还是尽量使用这个默认值。

ServletFilter一直占用着请求处理线程,直到它完成任务。如果完成任务花费了很长时间,并发用户的数量就会超过线程数量,容器将会遇到超出线程的风险。如果发生这种情况,TOmcat就会将超出的请求堆放在一个内部服务器Socket中(其他容器的处理方式可能会有所不同)。如果继续进来更多的请求,它们将会遭到拒绝,直到有资源可以处理请求为止。

异步处理特性可以帮助你节省容器线程。这项特性适用于长时间运行的操作。它的工作是等待任务完成,并释放请求处理线程,以便另一个请求能够使用该线程。注意,异步支持只适用于长时间运行的任务,并且你想让用户知道任务的执行结果。如果只有长时间运行的任务,但用户不需要知道处理的结果,那么则只要提供一个RunnableExecutor,并立即返回。例如,如果需要产生一份报表,并在保镖准备就绪之后通过电子邮件将报表发送出去,那么就不适合使用异步处理特性了。相反,如果需要产生一份报表,并且报表完成之后要展示给用户看,那么就可以使用异步处理。

 

13.2、编写异步的ServletFilter

WebServletWebFilter注解类型可以包含新的asyncSupport属性,为了编写能够支持异步处理的ServletFilterasyncSupported属性必须设为true

@WebServlet(asyncSupported=true...)

@WebFilter(asyncSupported=true...)

另一种在配置文件中配置

<servlet>

<servlet-name>AsyncServlet</servlet-name>

<servlet-class>servlet.MyAsyncServlet</servlet-class>

</servlet>


13.3、编写异步的Servlet

编写异步的SerlvetFilter相对比较简单。如果你有一个任务需要相对比较长时间才能完成,最好创建一个异步的Servlet或者Filter,在异步的Servlet或者Filter类中需要完成以下工作:

1、在ServletRequest中调用startAsync方法。startAsync会返回一个AsyncContext

2、在AsyncContext中调用setTimeout()方法,设置一个容器必须等待指定任务完成的毫秒数。这个步骤是可选的,但是如果没有设置这个时限,将会采用容器的默认时间。如果任务没能在规定实限内完成,将会抛出异常。

3、调用asyncContext.start方法,传递一个执行长时间任务的Runnable

4、任务完成时,通过Runnable调用asyncContext.complete方法或者asyncContext.dispatch方法。

下面是异步ServletdoGet或者doPost方法的主要内容:

final AsyncContext asyncContext = servletRequest.startAsync();

asyncContext.setTimeout( ... );

asyncContext.start(new Runnable(){

@Override

 public void run(){

//long running task

asyncContext.complete() or asyncContext.dispatch()

}

})

下面是个例子:

AsyncDispatchServlet.java

[html]  view plain  copy
 print ?
  1. package servlet;  
  2.   
  3. import java.io.IOException;  
  4.   
  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. @WebServlet(name="AsyncDispatchServlet",   
  12.         urlPatterns={"/asyncDispatch"},  
  13.         asyncSupported=true)  
  14. public class AsyncDispatchServlet extends HttpServlet {  
  15.       
  16.     private static final long serialVersionUID = 1L;  
  17.     @Override  
  18.     public void doGet(final HttpServletRequest request,  
  19.             HttpServletResponse response)  
  20.             throws ServletException, IOException{  
  21.         final AsyncContext asyncContext = request.startAsync() ;  
  22.         request.setAttribute("mainThread", Thread.currentThread().getName());  
  23.         asyncContext.setTimeout(5000);  
  24.         asyncContext.start(new Runnable(){  
  25.   
  26.             @Override  
  27.             public void run() {  
  28.                 try {  
  29.                     Thread.sleep(3000);  
  30.                 } catch (InterruptedException e) {  
  31.                     e.printStackTrace();  
  32.                 }  
  33.                 request.setAttribute("workerThread", Thread.currentThread().getName());  
  34.                 asyncContext.dispatch("/ThreadNames.jsp");  
  35.             }  
  36.               
  37.         });  
  38.     }  
  39. }  

ThreadNames.jsp

[html]  view plain  copy
 print ?
  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  
  2. <%  
  3. String path = request.getContextPath();  
  4. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";  
  5. %>  
  6.   
  7. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  8. <html>  
  9.   <head>  
  10.     <base href="<%=basePath%>">  
  11.       
  12.     <title>Asynchronous Servlet</title>  
  13.       
  14.     <meta http-equiv="pragma" content="no-cache">  
  15.     <meta http-equiv="cache-control" content="no-cache">  
  16.     <meta http-equiv="expires" content="0">      
  17.     <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">  
  18.     <meta http-equiv="description" content="This is my page">  
  19.     <!-- 
  20.     <link rel="stylesheet" type="text/css" href="styles.css"> 
  21.     -->  
  22.   
  23.   </head>  
  24.     
  25.   <body>  
  26.     Main Thread:${mainThread}  
  27.     <br/>  
  28.     Worker Thread:${workerThread}  
  29.   </body>  
  30. </html>  

运行结果:



下面这个例子的Servlet每秒钟发送一次进程更新,以便用户能够追踪进程。它发送HTML响应和一个简单的JavaScript代码,用来更新一个HTML div元素。

AsyncCompleteServlet.java

[html]  view plain  copy
 print ?
  1. package servlet;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.PrintWriter;  
  5.   
  6. import javax.servlet.AsyncContext;  
  7. import javax.servlet.ServletException;  
  8. import javax.servlet.http.HttpServlet;  
  9. import javax.servlet.http.HttpServletRequest;  
  10. import javax.servlet.http.HttpServletResponse;  
  11.   
  12. public class AsyncCompleteServlet extends HttpServlet {  
  13.   
  14.     private static final long serialVersionUID = 1L;  
  15.       
  16.     @Override  
  17.     public void doGet(HttpServletRequest request, HttpServletResponse response)  
  18.             throws ServletException, IOException {  
  19.         response.setContentType("text/html");  
  20.         final PrintWriter writer = response.getWriter() ;  
  21.         writer.println("<html><head><title>" +   
  22.                 "Async Servlet</title></head>");  
  23.         writer.println("<body><div id='progress'></div>");  
  24.         final AsyncContext asyncContext = request.startAsync() ;  
  25.         asyncContext.setTimeout(60000);  
  26.         asyncContext.start(new Runnable(){  
  27.   
  28.             @Override  
  29.             public void run() {  
  30.                 System.out.println("new thread:" + Thread.currentThread());  
  31.                 for(int i=0; i<10; i++){  
  32.                     writer.println("<script>");  
  33.                     writer.println("document.getElementById(" +   
  34.                                 "'progress').innerHTML = '" +   
  35.                                 (i*10) + "% complete'");  
  36.                     writer.println("</script>") ;  
  37.                     writer.flush();  
  38.                     try {  
  39.                         Thread.sleep(1000);  
  40.                     } catch (InterruptedException e) {  
  41.                         e.printStackTrace();  
  42.                     }  
  43.                 }  
  44.                 writer.println("<script>");  
  45.                 writer.println("document.getElementById(" +   
  46.                         "'progress').innerHTML='DONE'");  
  47.                 writer.println("</script>");  
  48.                 writer.println("</body></html>");  
  49.                 asyncContext.complete();  
  50.             }  
  51.               
  52.         });  
  53.     }  
  54. }  

部署配置文件

[html]  view plain  copy
 print ?
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">  
  3.   <display-name>AsyncServlet</display-name>  
  4.   <servlet>  
  5.     <servlet-name>AsyncComplete</servlet-name>  
  6.     <servlet-class>servlet.AsyncCompleteServlet</servlet-class>  
  7.     <async-supported>true</async-supported>  
  8.   </servlet>  
  9.   <servlet-mapping>  
  10.     <servlet-name>AsyncComplete</servlet-name>  
  11.     <url-pattern>/asyncComplete</url-pattern>  
  12.   </servlet-mapping>  
  13. </web-app>  


运行结果:


13.4、异步监听器

除了支持ServletFilter执行异步操作之外,Servlet3.0还新增了一个AsyncListener接口,以便通知用户在异步处理期间发生的情况。AsyncListener接口定义了以下方法,当某个事件发生时,其中某一个方法就会被调用。

void onStartAsync(AsyncEvent event)

在刚启动一个异步操作时调用这个方法

void onComplete(AsyncEvent event)

当一个异步操作已经完成时调用这个方法

void onError(AsyncEvent event)

当一个异步操作失败时调用这个方法

void onTimeout(AsyncEvent event)

当一个异步操作已经超时的时候调用这个方法,即当它没能在规定时限内完成的时候。

4个方法都会收到一个AsyncEvent事件,你可以分别通过调用它的getAsyncContextgetSuppliedReqeustgetSuppliedResponse方法从中获得相关的AsyncContextServletRequestServletResponse实例。

下面的例子和其他Web监听器不同的是,它没有用@WebListener标注AsyncListener的实现。

MyAsyncListener.java

[html]  view plain  copy
 print ?
  1. package listener;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import javax.servlet.AsyncEvent;  
  6. import javax.servlet.AsyncListener;  
  7.   
  8. public class MyAsyncListener implements AsyncListener{  
  9.   
  10.     @Override  
  11.     public void onComplete(AsyncEvent arg0) throws IOException {  
  12.         // TODO Auto-generated method stub  
  13.         System.out.println("MyAsyncListener.onComplete()");  
  14.     }  
  15.   
  16.     @Override  
  17.     public void onError(AsyncEvent arg0) throws IOException {  
  18.         // TODO Auto-generated method stub  
  19.         System.out.println("MyAsyncListener.onError()");  
  20.     }  
  21.   
  22.     @Override  
  23.     public void onStartAsync(AsyncEvent arg0) throws IOException {  
  24.         // TODO Auto-generated method stub  
  25.         System.out.println("MyAsyncListener.onStartAsync()");  
  26.     }  
  27.   
  28.     @Override  
  29.     public void onTimeout(AsyncEvent arg0) throws IOException {  
  30.         // TODO Auto-generated method stub  
  31.         System.out.println("MyAsyncListener.onTimeout()");  
  32.     }  
  33.   
  34. }  

AsyncListenerServlet.java

[html]  view plain  copy
 print ?
  1. package servlet;  
  2.   
  3. import java.io.IOException;  
  4.   
  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. import listener.MyAsyncListener;  
  13. @WebServlet(name="AsyncListenerServlet",  
  14.         urlPatterns={"/asyncListener"},  
  15.         asyncSupported=true)  
  16. public class AsyncListenerServlet extends HttpServlet{  
  17.   
  18.     private static final long serialVersionUID = 1L;  
  19.       
  20.     @Override  
  21.     public void doGet(final HttpServletRequest request, HttpServletResponse response)  
  22.             throws ServletException, IOException {  
  23.         final AsyncContext asyncContext = request.startAsync() ;  
  24.         asyncContext.setTimeout(5000);  
  25.         asyncContext.addListener(new MyAsyncListener());  
  26.         asyncContext.start(new Runnable(){  
  27.   
  28.             @Override  
  29.             public void run() {  
  30.                 try {  
  31.                     Thread.sleep(3000);  
  32.                 } catch (InterruptedException e) {  
  33.                     e.printStackTrace();  
  34.                 }  
  35.                 String greeting = "hi from listener" ;  
  36.                 System.out.println("wait...");  
  37.                 request.setAttribute("greeting", greeting);  
  38.                 asyncContext.dispatch("/index.jsp");  
  39.             }  
  40.               
  41.         });  
  42.     }  
  43. }  
目录
相关文章
|
6月前
|
前端开发 编译器 Android开发
构建高效Android应用:探究Kotlin协程的异步处理机制
【4月更文挑战第2天】在现代移动应用开发中,提供流畅且响应迅速的用户体验是至关重要的。随着Android平台的发展,Kotlin语言凭借其简洁性和功能性编程的特点成为了主流选择之一。特别地,Kotlin协程作为一种新型的轻量级线程管理机制,为开发者提供了强大的异步处理能力,从而显著提升了应用程序的性能和响应速度。本文将深入探讨Kotlin协程在Android中的应用,分析其原理、实现以及如何通过协程优化应用性能。
|
25天前
|
前端开发 JavaScript API
掌握异步编程:提升JavaScript应用性能的关键
【10月更文挑战第5天】在JavaScript世界中,异步编程已成为提升应用性能的关键技能。本文深入探讨异步编程的概念、工具及最佳实践,介绍回调函数、Promises和async/await等机制,并讲解其优势与应用场景,如数据获取、文件操作和定时任务。通过实战技巧,帮助开发者避免回调地狱、优化错误处理,并合理使用Promise.all和async/await,从而编写出更高效、更健壮的代码。
|
30天前
|
Java Linux Go
协程的设计原理(一)
协程的设计原理(一)
28 0
|
3月前
|
开发者 Python
揭秘Django信号的神奇之处:如何解锁异步处理与插件化的秘密武器?
【8月更文挑战第31天】在现代Web开发中,Django信号作为一种强大的机制,支持异步处理与插件化扩展,使开发者能够在不改动现有代码的情况下,在关键事件点插入自定义逻辑。本文详细介绍了Django信号的概念、基本用法及其在异步任务处理与插件开发中的具体应用,展示了如何利用信号监听模型变化并触发相应操作,从而提升应用性能与灵活性,预示着信号机制在未来Web开发中的重要潜力。
24 0
|
3月前
|
前端开发 JavaScript
前端搞懂事件循环机制
【8月更文挑战第3天】前端搞懂事件循环机制
46 1
|
5月前
|
安全 API 调度
异步编程中常见的问题和处理方式
【6月更文挑战第23天】在python中`asyncio` 提供PriorityQueue和LifoQueue,用于不同检索策略。异步编程需注意任务调度、错误处理和资源管理,以提高响应性和避免阻塞。
122 7
异步编程中常见的问题和处理方式
|
4月前
|
调度
【浅入浅出】Qt多线程机制解析:提升程序响应性与并发处理能力
在学习QT线程的时候我们首先要知道的是QT的主线程,也叫GUI线程,意如其名,也就是我们程序的最主要的一个线程,主要负责初始化界面并监听事件循环,并根据事件处理做出界面上的反馈。但是当我们只限于在一个主线程上书写逻辑时碰到了需要一直等待的事件该怎么办?它的加载必定会带着主界面的卡顿,这时候我们就要去使用多线程。
148 6
|
6月前
|
存储 前端开发 rax
|
消息中间件 Linux
事件驱动的I/O模型是什么意思?底层原理是什么?
事件驱动的I/O模型是什么意思?底层原理是什么?
194 0
【异步电路碎碎念5】 —— 跨异步处理的几个注意事项
【异步电路碎碎念5】 —— 跨异步处理的几个注意事项
132 0