SpringMVC异步处理的 5 种方式示例详解

前段时间研究了下 diamond 的原理,其中有个重要的知识点是长连接的实现,用到了 servlet 的异步处理。异步处理最大的好处是可以提高并发量,不阻塞当前线程。其实 Spring MVC 也支持了异步处理,本文记录下相关的技术点。

异步处理 demo

如果要启用异步返回,需要开启 @EnableAsync。如下的代码中,使用 DeferredResult 进行异步处理。

请求进来后,首先创建 DeferredResult 对象,设置超时时间为 60 秒。然后指定DeferredResult 在异步完成和等待超时时的回调。同步的处理只需要创建异步任何,然后返回DeferredResult 即可。这样 Spring MVC 处理完此次请求后,不会立即返回 response 给客户端,会一直等待DeferredResult 处理完成。如果DeferredResult 没有在 60 秒内处理完成,就会触发超时,然后返回 response 给客户端。

@RequestMapping(value = "/async/demo")
public DeferredResult<String> async(){
 // 创建 DeferredResult,设置超时时间 60s
 DeferredResult<String> deferredResult = new DeferredResult<>((long)60 * 1000);

 String uuid = UUID.randomUUID().toString();
 Runnable callback = () -> manager.remove(deferredResult, uuid);
 // 设置完成和超时的回调
 deferredResult.onCompletion(callback);
 deferredResult.onTimeout(callback);

 // 创建异步任务
 manager.addAsyncTask(deferredResult, uuid);

 // 同步返回 DeferredResult
 return deferredResult;
}

对于异步任务来说,需要持有DeferredResult 对象。在异步处理结束时,需要手动调用DeferredResult.setResult完成输出。调用setResult 时,数据输出写到客户端,然后触发异步完成事件执行回调。

task.getDeferredResult().setResult(ConfigJsonUtils.toJsonString(map));

使用 DeferredResult 进行异步处理

DeferredResult 这个类代表延迟结果。DeferredResult 可以用在异步任务中,其他线程能够获取DeferredResult并设置DeferredResult 的返回数据。通常可以使用线程池、队列等配合DeferredResult 实现异步处理。

根据官方描述,Spring MVC 处理流程如下:

  1. 把 controller 返回的 DeferredResult 保存在内存队列或集合当中;
  2. Spring MVC 调用 request.startAsync(),开启异步;
  3. DispatcherServlet 和所有的 Filter 退出当前请求线程;
  4. 业务应用在异步线程中设置 DeferredResult 的返回值, Spring MVC 会再次发送请求;
  5. DispatcherServlet 再次被调用,并使用 DeferredResult 的返回值;

使用 Callable 进行异步处理

使用 Callable 进行异步处理与 DeferredResult 类似。不同的是,Callable 会交给系统指定的 TaskExecutor 执行。

根据官方描述,Spring MVC 处理流程如下:

  1. controller 返回 Callable ;
  2. Spring MVC 调用 request.startAsync(),开启异步,提交 Callable 到一个任务线程池 ;
  3. DispatcherServlet 和所有的 Filter 退出当前请求线程;
  4. 业务应用在异步线程中 返回值, Spring MVC 会再次发送请求;
  5. DispatcherServlet 再次被调用,并使用 Callable 的返回值;
@RequestMapping(value = "/async/demo")
public Callable<String> async(){
 Callable<String> callable = () -> String.valueOf(System.currentTimeMillis());
 // 同步返回
 return callable;
}

使用 ListenableFuture 进行异步处理

ListenableFuture 作为返回值,与DeferredResult 类似。也需要使用者自行处理异步线程,但不支持超时、完成回调,需要自行处理。

@RequestMapping(value = "/async/demo")
public ListenableFuture<String> async(){
 ListenableFutureTask<String> ListenableFuture= new ListenableFutureTask<>(() -> {
  return String.valueOf(System.currentTimeMillis());
 });
 Executors.newSingleThreadExecutor().submit(ListenableFuture);
 return ListenableFuture;
}

使用 ResponseBodyEmitter 进行异步处理

DeferredResult 和 Callable 都只能返回一个异步值。如果需要返回多个对象,就要使用 ResponseBodyEmitter。返回的每个对象都会被 HttpMessageConverter 处理并写回输出流。如果希望设置更多返回数据,如 header、status 等,可以把 ResponseBodyEmitter 作为 ResponseEntity 的实体数据返回。

@RequestMapping("/async/responseBodyEmitter")
public ResponseBodyEmitter responseBodyEmitter(){
 ResponseBodyEmitter responseBodyEmitter=new ResponseBodyEmitter();

 Executors.newSingleThreadExecutor().submit(() -> {
  try {
   responseBodyEmitter.send("demo");
   responseBodyEmitter.send("test");
   responseBodyEmitter.complete();
  } catch (Exception ignore) {}
 });

 return responseBodyEmitter;
}

使用 StreamingResponseBody 进行异步处理

如果希望跳过返回值的自动转换,直接把输出流写入OutputStream,可以使用 StreamingResponseBody。也可以作为 ResponseEntity 的实体数据返回。

@RequestMapping("/async/streamingResponseBody")
public StreamingResponseBody streamingResponseBody(){
 StreamingResponseBody streamingResponseBody = outputStream -> {
  Executors.newSingleThreadExecutor().submit(() -> {
   try {
    outputStream.write("<html>streamingResponseBody</html>".getBytes());
   } catch (IOException ignore) {}
  });
 };
 return streamingResponseBody;
}

各种处理方式的对比


可返回次数


数据转换


回调


线程池


DeferredResult


1 次



完成、超时


自行处理


Callable


1 次




系统处理


ListenableFuture


1 次




自行处理


ResponseBodyEmitter


多次




自行处理


StreamingResponseBody


多次




自行处理

到此这篇关于SpringMVC异步处理的 5 种方式的文章就介绍到这了,更多相关SpringMVC异步处理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • springmvc利用jquery.form插件异步上传文件示例

    需要的下载文件: jQuery.form.js jquery.js commons-fileupload.jar commons-io.jar 示例图片 pom.xml <!-- 文件上传 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3</ve

  • springmvc处理异步请求的示例

    springmvc 3.2开始就支持servlet3.0的异步请求.平常我们请求一个controller一般都是同步的,如果在代码执行中,遇到耗时的业务操作,那servlet容器线程就会被锁死,当有其他请求进来的时候就会受堵了. springmvc3.2之后支持异步请求,能够在controller中返回一个Callable或者DeferredResult.当返回Callable的时候,大概的执行过程如下: 当controller返回值是Callable的时候,springmvc就会启动一个线程将

  • SpringMVC环境下实现的Ajax异步请求JSON格式数据

    一 环境搭建 首先是常规的spring mvc环境搭建,不用多说,需要注意的是,这里需要引入jackson相关jar包,然后在spring配置文件"springmvc-servlet.xml"中添加json解析相关配置,我这里的完整代码如下: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schem

  • SpringMVC异步处理操作(Callable和DeferredResult)

    官方文档中说DeferredResult和Callable都是为了异步生成返回值提供基本的支持.简单来说就是一个请求进来,如果你使用了DeferredResult或者Callable,在没有得到返回数据之前,DispatcherServlet和所有Filter就会退出Servlet容器线程,但响应保持打开状态,一旦返回数据有了,这个DispatcherServlet就会被再次调用并且处理,以异步产生的方式,向请求端返回值. 这么做的好处就是请求不会长时间占用服务连接池,提高服务器的吞吐量. Ca

  • SpringMVC使用MultipartFile 实现异步上传方法介绍

    目的是实现异步上传 1.添加pom依赖 添加pom依赖,因为用的ajax,数据需要转成json的格式进行传输,所以还有加入一个JSON jar包: <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency>

  • SpringMVC异步处理的 5 种方式示例详解

    前段时间研究了下 diamond 的原理,其中有个重要的知识点是长连接的实现,用到了 servlet 的异步处理.异步处理最大的好处是可以提高并发量,不阻塞当前线程.其实 Spring MVC 也支持了异步处理,本文记录下相关的技术点. 异步处理 demo 如果要启用异步返回,需要开启 @EnableAsync.如下的代码中,使用 DeferredResult 进行异步处理. 请求进来后,首先创建 DeferredResult 对象,设置超时时间为 60 秒.然后指定DeferredResult

  • C#获取本地IP的四种方式示例详解

    1.第一种方式 采用System.Net.Dns的GetHostAddress的方式,具体请看代码: /// <summary> /// 网络不通畅可以获取 /// 不过能获取到具体的IP /// </summary> /// <returns></returns> public static List<IPAddress> GetByGetHostAddresses() { try { IPAddress[] adds = Dns.GetHos

  • node运行js获得输出的三种方式示例详解

    一.通过console.log输出(我最喜欢的) 1.js脚本 1.js var arguments = process.argv.splice(2); //获得入参 var a= arguments[0]; 取第一个 console.log(a) //输出 2.python脚本 test_1.py import os print(os.popen('node 1.js fuck').read()) #打印结果fuck 二.通过文件读写获取 1.js脚本 1.js //npm环境别忘了装了 va

  • Vue自定义组件的四种方式示例详解

    四种组件定义方式都存在以下共性(血泪史) 规则: 1.组件只能有一个根标签 2.记住两个词全局和局部 3.组件名称命名中'-小写字母'相当于大写英文字母(hello-com 相当于 helloCom) 而对于在HTML中自定义组件的时候有4种写法,不过也只是殊途同归,都是用template属性对应的只有一个根标签的HTML代码. 1.全局组件 定义方式示例: Vue.component("hello-component",{ props:["message"], t

  • vue+webpack实现异步加载三种用法示例详解

    1.第一例 const Home = resolve => { import("@/components/home/home.vue").then( module => { resolve(module) } } 注:(上面import的时候可以不写后缀) export default [{ path: '/home', name:'home', component: Home, meta: { requireAuth: true, // 添加该属性可以判断出该页面是否需要

  • Vue路由切换的两种方式示例详解

    目录 Vue路由切换的两种方式 1. 标签切换 1.1 <a>标签切换 1.2 路径切换 1.3 path切换 1.4 name切换(推荐) 2. js切换 Vue路由切换的两种方式 1. 标签切换 1.1 <a>标签切换 语法:<a href=“# + 路由路径”>标签内容</a> 例子: 路由规则为: const router = new VueRouter({ //路由对象数组 routes: [{ path: '/login', component:

  • c#使用多线程的几种方式示例详解

    (1)不需要传递参数,也不需要返回参数 ThreadStart是一个委托,这个委托的定义为void ThreadStart(),没有参数与返回值. 复制代码 代码如下: class Program { static void Main(string[] args) { for (int i = 0; i < 30; i++) { ThreadStart threadStart = new ThreadStart(Calculate); Thread thread = new Thread(thr

  • C#开启线程的四种方式示例详解

    一.异步委托开启线程 public static void Main(string[] args){ Action<int,int> a=add; a.BeginInvoke(3,4,null,null);//前两个是add方法的参数,后两个可以为空 Console.WriteLine("main()"); Console.ReadKey(); } static void add(int a,int b){ Console.WriteLine(a+b); } 运行结果: 如

  • JS中原始值和引用值的储存方式示例详解

    在ECMAscript中,变量可以存放两种类型的值,即原始值和引用值 原始值指的是代表原始数据类型的值,也叫基本数据类型,包括:Number.Stirng.Boolean.Null.Underfined 引用值指的是复合数据类型的值,包括:Object.Function.Array.Date.RegExp 根据数据类型不同,有的变量储存在栈中,有的储存在堆中.具体区别如下: 原始变量及他们的值储存在栈中,当把一个原始变量传递给另一个原始变量时,是把一个栈房间的东西复制到另一个栈房间,且这两个原始

  • Spring加载properties文件的两种方式实例详解

    在项目中如果有些参数经常需要修改,或者后期可能需要修改,那我们最好把这些参数放到properties文件中,源代码中读取properties里面的配置,这样后期只需要改动properties文件即可,不需要修改源代码,这样更加方便.在Spring中也可以这么做,而且Spring有两种加载properties文件的方式:基于xml方式和基于注解方式.下面分别讨论下这两种方式. 1. 通过xml方式加载properties文件 我们以Spring实例化dataSource为例,我们一般会在beans

随机推荐