Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下:首先,Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理;接着,调用业务接口的某些方法,以完成业务处理;最后,根据处理的结果提交响应,Servlet 线程结束。其中第二步的业务处理通常是最耗时的,这主要体现在数据库操作,以及其它的跨网络调用等,在此过程中,Servlet 线程一直处于阻塞状态,直到业务方法执行完毕。在处理业务的过程中,Servlet 资源一直被占用而得不到释放,对于并发较大的应用,这有可能造成性能的瓶颈。对此,在以前通常是采用私有解决方案来提前结束 Servlet 线程,并及时释放资源。

Servlet 3.0 针对这个问题做了开创性的工作,现在通过使用 Servlet 3.0 的异步处理支持,之前的 Servlet 处理流程可以调整为如下的过程:首先,Servlet 接收到请求之后,可能首先需要对请求携带的数据进行一些预处理;接着,Servlet 线程将请求转交给一个异步线程来执行业务处理,线程本身返回至容器,此时 Servlet 还没有生成响应数据,异步线程处理完业务以后,可以直接生成响应数据(异步线程拥有 ServletRequest 和 ServletResponse 对象的引用),或者将请求继续转发给其它 Servlet。如此一来, Servlet 线程不再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即返回。

异步处理特性可以应用于 Servlet 和过滤器两种组件,由于异步处理的工作模式和普通工作模式在实现上有着本质的区别,因此默认情况下,Servlet 和过滤器并没有开启异步处理特性,如果希望使用该特性,则必须按照如下的方式启用:

  1. 对于使用传统的部署描述文件 (web.xml) 配置 Servlet 和过滤器的情况,Servlet 3.0 为 <servlet> 和 <filter> 标签增加了 <async-supported> 子标签,该标签的默认取值为 false,要启用异步处理支持,则将其设为 true 即可。以 Servlet 为例,其配置方式如下所示:

    1
    2
    3
    4
    5
    <servlet>
        <servlet-name>DemoServlet</servlet-name>
        <servlet-class>footmark.servlet.Demo Servlet</servlet-class>
        <async-supported>true</async-supported>
    </servlet>
  2. 对于使用 Servlet 3.0 提供的 @WebServlet 和 @WebFilter 进行 Servlet 或过滤器配置的情况,这两个注解都提供了 asyncSupported 属性,默认该属性的取值为 false,要启用异步处理支持,只需将该属性设置为 true 即可。以 @WebFilter 为例,其配置方式如下所示:
1
2
@WebFilter(urlPatterns = "/demo",asyncSupported = true)
public class DemoFilter implements Filter{...}

一个简单的模拟异步处理的 Servlet 示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@WebServlet(urlPatterns = "/demo", asyncSupported = true)
public class AsyncDemoServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws IOException, ServletException {
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("进入Servlet的时间:" + new Date() + ".");
        out.flush();
 
        //在子线程中执行业务调用,并由其负责输出响应,主线程退出
        AsyncContext ctx = req.startAsync();
        new Thread(new Executor(ctx)).start();
 
        out.println("结束Servlet的时间:" + new Date() + ".");
        out.flush();
    }
}
 
public class Executor implements Runnable {
    private AsyncContext ctx = null;
    public Executor(AsyncContext ctx){
        this.ctx = ctx;
    }
 
    public void run(){
        try {
            //等待十秒钟,以模拟业务方法的执行
            Thread.sleep(10000);
            PrintWriter out = ctx.getResponse().getWriter();
            out.println("业务处理完毕的时间:" + new Date() + ".");
            out.flush();
            ctx.complete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

除此之外,Servlet 3.0 还为异步处理提供了一个监听器,使用 AsyncListener 接口表示。它可以监控如下四种事件:

  1. 异步线程开始时,调用 AsyncListener 的 onStartAsync(AsyncEvent event) 方法;
  2. 异步线程出错时,调用 AsyncListener 的 onError(AsyncEvent event) 方法;
  3. 异步线程执行超时,则调用 AsyncListener 的 onTimeout(AsyncEvent event) 方法;
  4. 异步执行完毕时,调用 AsyncListener 的 onComplete(AsyncEvent event) 方法;

要注册一个 AsyncListener,只需将准备好的 AsyncListener 对象传递给 AsyncContext 对象的 addListener() 方法即可,如下所示:

1
2
3
4
5
6
7
AsyncContext ctx = req.startAsync();
ctx.addListener(new AsyncListener() {
    public void onComplete(AsyncEvent asyncEvent) throws IOException {
        // 做一些清理工作或者其他
    }
    ...
});

servlet异步处理机制的更多相关文章

  1. nacos 使用 servlet 异步处理客户端配置长轮询

    config 客户端 ClientWorker#ClientWorker 构造方法中启动定时任务 ClientWorker.LongPollingRunnable 长轮询的任务,在 run 方法的结尾 ...

  2. Servlet异步上传文件

    这里需要用到插件ajaxfileupload.js,jar包:commons-fileupload-1.3.2.jar,commons-io-2.5.jar 注意红色部分的字!!!! 1.创建一个we ...

  3. Android异步任务机制之AsycTask

    在Android中实现异步任务机制有两种方式,Handler和AsyncTask. 本篇就说说AsyncTask的异步实现. 1.什么时候使用 AsnyncTask 在上一篇文章已经说了,主线程主要负 ...

  4. Android异步消息处理机制

    安卓子线程无法直接更改UI,所以需要异步消息处理机制来解决 <?xml version="1.0" encoding="utf-8"?><Li ...

  5. Android_AsyncTask异步任务机制

    今天我们学习了 AsyncTack, 这是一个异步任务. 那么这个异步任务可以干什么呢? 因为只有UI线程,即主线程可以对控件进行更新操作.好处是保证UI稳定性,避免多线程对UI同时操作. 同时要把耗 ...

  6. Android 异步消息处理机制解析

    Android 中的异步消息处理主要由四个部分组成,Message.Handler.MessageQueue.Looper.下面将会对这四个部分进行一下简要的介绍. 1. Message: Messa ...

  7. Android多线程----异步消息处理机制之Handler详解

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  8. 【译】深入理解python3.4中Asyncio库与Node.js的异步IO机制

    转载自http://xidui.github.io/2015/10/29/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3python3-4-Asyncio%E5%BA%93% ...

  9. 【转】Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Andr ...

随机推荐

  1. oracle 11g RAC日志分布

    oracle 下的alert日志$ORACLE_BASE/diag/rdbms/orcl/orcl1/trace/alertorcl1.log grid 下的 alert 日志 $ORACLE_HOM ...

  2. adb常用命令(转)

    1.Android Debug Bridge -adb常用命令 1.1简介 Android Debug Bridge,我们一般简称为adb,主要存放在sdk安装目录下的platform-tools文件 ...

  3. 使用VSCode调试Jest

    0. 环境 Node版本:8.12.0 操作系统:windows10 1. 配置launch.json { "version": "0.2.0", " ...

  4. div变成输入框

    <style> #test{ width: 150px;; min-height:20px; max-height:70px; outline: 0; border: 1px solid ...

  5. vue cli 3 lintOnSave 配置有时无效问题

    一个使用vue cli 3.2创建的项目,创建时未开启 lintOnSave,后来希望开启并设置为 lintOnSave: 'error',但配置不生效. 解决方法1:新创建项目(此时vue cli ...

  6. StringBuffer的delete方法与deleteCharAt方法的区别。

    delete方法与deleteCharAt两个方法都是用来删除StringBuffer字符串指定索引字符的方法, delete(int  begin,int  end)有两个参数,使用时删除索引从be ...

  7. YARN详解

    1.1      分布式资源调度框架 1.2.1          yarn的概念 Apache Hadoop YARN (Yet Another Resource Negotiator,另一种资源协 ...

  8. 2018-2019-2 20175317 实验二《Java面向对象程序设计》实验报告

    2018-2019-2 20175317 实验二<Java面向对象程序设计>实验报告 一.实验步骤及内容 面向对象程序设计-1 参考 http://www.cnblogs.com/roce ...

  9. linux日常使用指令总结

    linux 日常指令总结(ubuntu): ls -lha 所有文件及权限 df -h 系统磁盘空间 du -sh 当前目录大小 du -ah --max-depth=1 显示目录下所有的文件和文件夹 ...

  10. promise用法十道题

    JS是单线程语言,多数的网站不需要大量计算,程序耗时主要是在磁盘I/O和网络I/O上 ,虽然固态硬盘SSD读取很快,但是和CPU比起来却不在一个数量级上,而且网络上的一个数据包来回时间更慢,所以一些C ...