Spring-MVC异步请求之Servlet异步

  在Servlet3.0的规范中新增了对异步请求的支持,SpringMVC又在此基础上对异步请求提供了方便。异步请求是在处理比较耗时的业务时先将request返回,然后另起线程处理耗时的业务,处理完后在返回给用户。
  异步请求可以给我们带来很多方便,最直接的用法就是处理耗时的业务,比如,需要查询数据库,需要调用别的服务器来处理等情况下可以先将请求返回给客户端,然后启用新线程处理耗时业务。如果我们合适的扩展可以实现订阅者模式的消息订阅功能,比如,当有异常情况发生时可以主动将相关信息发送给运维人员,还有现在的很多邮箱自动回复都是使用这种技术。
  Http协议是单向的,只能客户端自己拉不能服务器主动推,Servlet对异步请求的支持并没有修改Http,而是对Http的巧妙利用。异步请求的核心原理主要分为两大类,一类是轮询,另一类是长连接。轮询就是定时自动发起请求检查有没有需要返回的数据,这种对资源浪费比较大。长连接的原理是客户端发起请求,服务端处理并返回后并不结束连接,这样就可以在后面再次返回给客户端数据。Servlet对异步请求的支持其实采用的是长连接的方式,也就是说,异步请求中在原始的请求返回的时候并没有关闭连接,关闭的只是处理请求的那个县城,只有在异步请求全部处理完之后才会关闭连接。

Servlet3.0对异步请求的支持

在Servlet3.0规范中使用异步处理请求非常简单,只需要在请求处理过程中调用request的startAsync返回AsyncContext。

  什么是AsyncContext在异步请求中充当着非常重要的角色,可以称为异步请求上下文也可以称为异步请求容器。类似于ServletContext.我们多次调用startAsync都是返回的同一个AsyncContext。代码如下:

   public interface AsyncContext {
    String ASYNC_REQUEST_URI = "javax.servlet.async.request_uri";
    String ASYNC_CONTEXT_PATH = "javax.servlet.async.context_path";
    String ASYNC_PATH_INFO = "javax.servlet.async.path_info";
    String ASYNC_SERVLET_PATH = "javax.servlet.async.servlet_path";
    String ASYNC_QUERY_STRING = "javax.servlet.async.query_string";

    ServletRequest getRequest();

    ServletResponse getResponse();

    boolean hasOriginalRequestAndResponse();

    void dispatch();

    void dispatch(String var1);

    void dispatch(ServletContext var1, String var2);

    void complete();

    void start(Runnable var1);

    void addListener(AsyncListener var1);

    void addListener(AsyncListener var1, ServletRequest var2, ServletResponse var3);

     T createListener(Class var1) throws ServletException;

    void setTimeout(long var1);

    long getTimeout();
}

getResponse() 用于获取response。dispatch用于分发新地址。complete用于通知容器已经处理完了,start方法用于启动实际处理线程,addListener用于添加监听器;setTimeout方法用于修改超时时间。

Servlet3.0处理异步请求实例

@WebServlet(
name = “WorkServlet”,
urlPatterns = “/work”,
asyncSupported = true
)
public class WorkServlet extends HttpServlet {
private static final long serialVersionUID =1L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //设置ContentType,关闭缓存
    resp.setContentType("text/plain;charset=UTF-8");
    resp.setHeader("Cache-Control","private");
    resp.setHeader("Pragma","no-cache");
    final PrintWriter writer= resp.getWriter();
    writer.println("老师检查作业了");
    writer.flush();
    List zuoyes=new ArrayList();
    for (int i = 0; i < 10; i++) {
        zuoyes.add("zuoye"+i);;
    }
    final AsyncContext ac=req.startAsync();//开启异步请求
    doZuoye(ac,zuoyes);
    writer.println("老师布置作业");
    writer.flush();
}

private void doZuoye(final AsyncContext ac, final List zuoyes) {
    ac.setTimeout(1*60*60*1000L);
    ac.start(new Runnable() {
        @Override
        public void run() {
            //通过response获得字符输出流
            try {
                PrintWriter writer=ac.getResponse().getWriter();
                for (String zuoye:zuoyes) {
                    writer.println("\""+zuoye+"\"请求处理中");
                    Thread.sleep(1*1000L);
                    writer.flush();
                }
                ac.complete();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

}
Spring-MVC异步请求之Servlet异步_第1张图片

异步请求监听器

在上面的程序是我们最基本的异步请求,不过不够完善。老师是需要思考宏观问题,所以在写完作业之后需要给老师汇报哪些题难,哪些题目有问题或者自己的这次经验总结,不过这些事不应该由做作业的学生来做,应该由专门的学习汇报员来统计分析。所以就有了监听器。

public class TeacherListener implements AsyncListener {
    final SimpleDateFormat formatter=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    @Override
    public void onComplete(AsyncEvent event) throws IOException {
        System.out.println("在"+formatter.format(new Date())+"工作处理完成");
    }

    @Override
    public void onTimeout(AsyncEvent event) throws IOException {
        System.out.println("在"+formatter.format(new Date())+"工作超时");
    }

    @Override
    public void onError(AsyncEvent event) throws IOException {
        System.out.println("在"+formatter.format(new Date())+"工作处理错误");
    }

    @Override
    public void onStartAsync(AsyncEvent event) throws IOException {
        System.out.println("在"+formatter.format(new Date())+"工作处理开始");
    }
}

所有代码具体参照github地址
https://github.com/lzggsimida123/ServletAsync

更多交流请扫我的技术公众号

Spring-MVC异步请求之Servlet异步_第2张图片

你可能感兴趣的:(tomcat,spring,MVC)