本篇文章我们以SpringBoot中异步的使用(包括:异步调用和异步方法两个维度)来进行讲解。
异步请求与同步请求
我们先通过一张图来区分一下异步请求和同步请求的区别:
在上图中有三个角色:客户端、Web容器和业务处理线程。
两个流程中客户端对Web容器的请求,都是同步的。因为它们在请求客户端时都处于阻塞等待状态,并没有进行异步处理。
在Web容器部分,第一个流程采用同步请求,第二个流程采用异步回调的形式。
通过异步处理,可以先释放容器分配给请求的线程与相关资源,减轻系统负担,从而增加了服务器对客户端请求的吞吐量。但并发请求量较大时,通常会通过负载均衡的方案来解决,而不是异步。
Servlet3.0中的异步
Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求,即每一次Http请求都由一个线程从头到尾处理。当涉及到耗时操作时,性能问题便比较明显。
Servlet 3.0中提供了异步处理请求。可以先释放容器分配给请求的线程与相关资源,减轻系统负担,从而增加服务的吞吐量。
Servlet 3.0的异步是通过AsyncContext对象来完成的,它可以从当前线程传给另一个线程,并归还初始线程。新的线程处理完业务可以直接返回结果给客户端。
AsyncContext对象可以从HttpServletRequest中获取:
@RequestMapping("/async") public void async(HttpServletRequest request) { AsyncContext asyncContext = request.getAsyncContext(); }
在AsyncContext中提供了获取ServletRequest、ServletResponse和添加监听(addListener)等功能:
public interface AsyncContext { ServletRequest getRequest(); ServletResponse getResponse(); void addListener(AsyncListener var1); void setTimeout(long var1); // 省略其他方法 }
不仅可以通过AsyncContext获取Request和Response等信息,还可以设置异步处理超时时间。通常,超时时间(单位毫秒)是需要设置的,不然无限等下去不就与同步处理一样了。
通过AsyncContext的addListener还可以添加监听事件,用来处理异步线程的开始、完成、异常、超时等事件回调。
addListener方法的参数AsyncListener的源码如下:
public interface AsyncListener extends EventListener { // 异步执行完毕时调用 void onComplete(AsyncEvent var1) throws IOException; // 异步线程执行超时调用 void onTimeout(AsyncEvent var1) throws IOException; // 异步线程出错时调用 void one rror(AsyncEvent var1) throws IOException; // 异步线程开始时调用 void onStartAsync(AsyncEvent var1) throws IOException; }
通常,异常或超时时返回调用方错误信息,而异常时会处理一些清理和关闭操作或记录异常日志等。
基于Servlet方式实现异步请求
下面直接看一个基于S本文来源gaodaimacom搞#^代%!码&网*ervlet方式的异步请求示例:
@GetMapping(value = "/email/send") public void servletReq(HttpServletRequest request) { AsyncContext asyncContext = request.startAsync(); // 设置监听器:可设置其开始、完成、异常、超时等事件的回调处理 asyncContext.addListener(new AsyncListener() { @Override public void onTimeout(AsyncEvent event) { System.out.println("处理超时了..."); } @Override public void onStartAsync(AsyncEvent event) { System.out.println("线程开始执行"); } @Override public void one rror(AsyncEvent event) { System.out.println("执行过程中发生错误:" + event.getThrowable().getMessage()); } @Override public void onComplete(AsyncEvent event) { System.out.println("执行完成,释放资源"); } }); //设置超时时间 asyncContext.setTimeout(6000); asyncContext.start(new Runnable() { @Override public void run() { try { Thread.sleep(5000); System.out.println("内部线程:" + Thread.currentThread().getName()); asyncContext.getResponse().getWriter().println("async processing"); } catch (Exception e) { System.out.println("异步处理发生异常:" + e.getMessage()); } // 异步请求完成通知,整个请求完成 asyncContext.complete(); } }); //此时request的线程连接已经释放了 System.out.println("主线程:" + Thread.currentThread().getName()); }