本文作者:黄少存,叩丁狼高级讲师。原创文章,转载请注明出处。
前言:说到异步请求咱们得先知道何为同步请求,从而对比来理解异步请求.同步请求简述, 浏览器发送请求->web服务器开启线程处理请求->处理完把结果返回给浏览器. 咱们来看下程序中同步请求的业务逻辑图.
对于同步操作来说,web服务器开启请求处理线程之后需要等待业务的处理完成才能做响应.对于同步请求来说这是理所当然的事情. 一般这样的操作也足够.但是如果有特殊要求,比如高并发,处理业务时间长的操作比较多,那会出现什么问题呢?
由于请求处理线程的总数是有限的,以上特殊请求会出现请求线程不够用的请求,因为可能都处于阻塞状态,影响服务器的吞吐量.所以就得使用异步请求,这也是高性能的关键.
对于异步请求操作来说,web 服务器的请求处理线程在处理请求上大大减少了时间,仅仅是接收了请求之后唤醒了新的业务处理线程,然后就结束了.这样的话,请求处理线程就可以留出多余的时间去处理其他的请求.提高了服务器的吞吐量.
既然异步请求可以提高处理能力,那如何实现异步请求呢?
其实 jetty 服务器就实现了异步请求,只是那是比较早期的操作了,现在有些框架也是有异步模块的,比如 spring mvc,但在 JavaWeb 中,其最根本的还是 Servlet3.0 的异步请求规范.接下来咱们就来看下servlet 原生态的异步请求实现.
步骤:
开启异步请求支持有两种方式.
// 配置资源路径和开启异步请求支持
@WebServlet(urlPatterns = {"/async"}, asyncSupported = true)
public class AsyncServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
//获取异步请求上下文对象
AsyncContext async = req.startAsync();
// 设置异步处理超时时间,默认30秒
async.setTimeout(40 * 1000);
// 启动异步处理,关键操作
async.start(() -> {
try {
// 模拟业务处理耗时
Thread.sleep(10 * 1000);
req.setAttribute("msg", "异步处理后的数据" + new Date().toLocaleString());
// 将请求转发到 index.jsp 页面
async.dispatch("/async.jsp");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 直接显示到页面的数据,不需等待以上业务处理
System.out.println("请求处理线程释放:" + new Date().toLocaleString());
}
}
async.jsp 页面书写
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
异步请求
发起请求
异步请求数据: ${msg}
Servlet 3.0 为异步处理提供了一个监听器,使用 AsyncListener 接口表示。此接口负责管理异步事件,它可以监控如下四种事件:
// 配置资源路径和开启异步请求支持
@WebServlet(urlPatterns = {"/async"}, asyncSupported = true)
public class AsyncServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
//获取异步请求上下文对象
AsyncContext async = req.startAsync();
async.addListener(new MyAsyncListener());
//...
}
public class MyAsyncListener implements AsyncListener {
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("完成后清理操作等..");
}
public void onTimeout(AsyncEvent event) throws IOException {
System.out.println("超时处理..");
}
public void onError(AsyncEvent event) throws IOException {
}
public void onStartAsync(AsyncEvent event) throws IOException {
}
}