1-介绍

Servlet 3中的异步支持为在另一个线程中处理HTTP请求提供了可能性。当有一个长时间运行的任务时,这是特别有趣的,因为当另一个线程处理这个请求时,容器线程被释放,并且可以继续为其他请求服务。

这个主题已经解释了很多次,Spring框架提供的关于这个功能的类似乎有一点混乱——在一个Controller中返回Callable 和 DeferredResult。

在这篇文章中,我将实施这两个例子,以显示其差异。

这里所显示的所有示例都包括执行一个控制器,该控制器将执行一个长期运行的任务,然后将结果返回给客户机。长时间运行的任务由taskservice处理:

@Service
public class TaskServiceImpl implements TaskService {
private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override
public String execute() {
try {
Thread.sleep(5000);
logger.info("Slow task executed");
return "Task finished";
} catch (InterruptedException e) {
throw new RuntimeException();
}
}
}

这个web应用是用Spring Boot创建的,我们将执行下面的类来运行我们的例子:

@SpringBootApplication
public class MainApp { public static void main(String[] args) {
SpringApplication.run(MainApp.class, args);
}
}

2-阻塞的Controller

在这个例子中,一个请求到达控制器。servlet线程不会被释放,直到长时间运行的方法被执行,我们退出@requestmapping注释的方法。

@RestController
public class BlockingController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final TaskService taskService; @Autowired
public BlockingController(TaskService taskService) {
this.taskService = taskService;
} @RequestMapping(value = "/block", method = RequestMethod.GET, produces = "text/html")
public String executeSlowTask() {
logger.info("Request received");
String result = taskService.execute();
logger.info("Servlet thread released"); return result;
}
}

如果我们运行这个例子http://localhost:8080/block,在日志里我们会发现servlet request不会被释放,直到长时间的任务执行完(5秒后)。

2015-07-12 12:41:11.849  [nio-8080-exec-6] x.s.web.controller.BlockingController    : Request received
2015-07-12 12:41:16.851 [nio-8080-exec-6] x.spring.web.service.TaskServiceImpl : Slow task executed
2015-07-12 12:41:16.851 [nio-8080-exec-6] x.s.web.controller.BlockingController : Servlet thread released

3-返回Callable

在这个例子中,不是直接返回的结果,我们将返回一个Callable:

@RestController
public class AsyncCallableController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final TaskService taskService; @Autowired
public AsyncCallableController(TaskService taskService) {
this.taskService = taskService;
} @RequestMapping(value = "/callable", method = RequestMethod.GET, produces = "text/html")
public Callable<String> executeSlowTask() {
logger.info("Request received");
Callable<String> callable = taskService::execute;
logger.info("Servlet thread released"); return callable;
}
}

返回Callable意味着Spring MVC将调用在不同的线程中执行定义的任务。Spring将使用TaskExecutor来管理线程。在等待完成的长期任务之前,servlet线程将被释放。

2015-07-12 13:07:07.012  [nio-8080-exec-5] x.s.w.c.AsyncCallableController          : Request received
2015-07-12 13:07:07.013 [nio-8080-exec-5] x.s.w.c.AsyncCallableController : Servlet thread released
2015-07-12 13:07:12.014 [ MvcAsync2] x.spring.web.service.TaskServiceImpl : Slow task executed

你可以看到我们在长时间运行的任务执行完毕之前就已经从servlet返回了。这并不意味着客户端收到了一个响应。与客户端的通信仍然是开放的等待结果,但接收到的请求的线程已被释放,并可以服务于另一个客户的请求。

4-返回DeferredResult

首先,我们需要创建一个deferredresult对象。此对象将由控制器返回。我们将完成和Callable相同的事,当我们在另一个线程处理长时间运行的任务的时候释放servlet线程。

@RestController
public class AsyncDeferredController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final TaskService taskService; @Autowired
public AsyncDeferredController(TaskService taskService) {
this.taskService = taskService;
} @RequestMapping(value = "/deferred", method = RequestMethod.GET, produces = "text/html")
public DeferredResult<String> executeSlowTask() {
logger.info("Request received");
DeferredResult<String> deferredResult = new DeferredResult<>();
CompletableFuture.supplyAsync(taskService::execute)
.whenCompleteAsync((result, throwable) -> deferredResult.setResult(result));
logger.info("Servlet thread released"); return deferredResult;
}
}

所以,返回DeferredResult和返回Callable有什么区别?不同的是这一次线程是由我们管理。创建一个线程并将结果set到DeferredResult是由我们自己来做的。

用completablefuture创建一个异步任务。这将创建一个新的线程,在那里我们的长时间运行的任务将被执行。也就是在这个线程中,我们将set结果到DeferredResult并返回。

是在哪个线程池中我们取回这个新的线程?默认情况下,在completablefuture的supplyasync方法将在forkjoin池运行任务。如果你想使用一个不同的线程池,你可以通过传一个executor到supplyasync方法:

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

如果我们运行这个例子,我们将得到如下结果:

2015-07-12 13:28:08.433  [io-8080-exec-10] x.s.w.c.AsyncDeferredController          : Request received
2015-07-12 13:28:08.475 [io-8080-exec-10] x.s.w.c.AsyncDeferredController : Servlet thread released
2015-07-12 13:28:13.469 [onPool-worker-1] x.spring.web.service.TaskServiceImpl : Slow task executed

5-结论

站在一定高度来看这问题,Callable和Deferredresult做的是同样的事情——释放容器线程,在另一个线程上异步运行长时间的任务。不同的是谁管理执行任务的线程。

文中涉及的代码spring-rest

翻译自Xavier Padró's Blog

理解Callable 和 Spring DeferredResult(翻译)的更多相关文章

  1. 【转】理解Callable 和 Spring DeferredResult

    http://www.cnblogs.com/aheizi/p/5659030.html 1-介绍 Servlet 3中的异步支持为在另一个线程中处理HTTP请求提供了可能性.当有一个长时间运行的任务 ...

  2. Spring DeferredResult 异步请求

    Spring DeferredResult 异步请求 一.背景 二.分析 三.实现要求 四.后端代码实现 五.运行结果 1.超时操作 2.正常操作 六.DeferredResult运行原理 六.注意事 ...

  3. spring技术翻译开始

    从今天开始,我会坚持每天花费两个小时来翻译一本英文书(当然自己觉得绝对算得上是经典),可能我英文水平有限,但也请路过的高人予以指点. 如果有翻译的出入很大,望各位告知,本人一定更改.决定翻译的目的有两 ...

  4. 入门级:理解FAT32文件系统(转载翻译)

    FAT(File Allocation Table ) 这个网页的目的是帮助你理解怎么样在微软FAT32文件系统下取得数据,处理的硬盘的大小通常在500M到几百G之间.FAT是一个相对简单和纯净的文件 ...

  5. Spring简单的小例子SpringDemo,用于初略理解什么是Spring以及JavaBean的一些概念

    一.开发前的准备 两个开发包spring-framework-3.1.1.RELEASE-with-docs.zip和commons-logging-1.2-bin.zip,将它们解压,然后把Spri ...

  6. Memcached理解笔2---XMemcached&Spring集成

    一.Memcached Client简要介绍 Memcached Client目前有3种: Memcached Client for Java SpyMemcached XMemcached 这三种C ...

  7. 深入理解Callable接口

    Callable接口: Callable,新启线程的一种方式,返回结果并且可能抛出异常的任务,在前面的新启线程的文章中用过,但是没有具体讲解 优点: 可以获取线程的执行结果,也称为返回值 通过与Fut ...

  8. HowToDoInJava Spring 教程·翻译完成

    原文:HowToDoInJava 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. ApacheCN 学习资源 目录 Spring 5 Spr ...

  9. Spring Web Async异步处理#Callable #DeferredResult

    Spring MVC 对于异步请求处理的两种方式 场景: Tomcat对于主线程性能瓶颈,当Tomcat请求并发数过多时,当线程数满时,就会出现请求等待Tomcat处理,这个时候可以使用子线程处理业务 ...

随机推荐

  1. TensorFlow------读取图片实例

    TensorFlow------读取图片实例: import tensorflow as tf import os def readpic(filelist): ''' 读取人物图片并转换成张量 :p ...

  2. chkconfig 检查、设置系统的各种服务

    chkconfig 检查.设置系统的各种服务 学习了:http://man.linuxde.net/chkconfig chkconfig --add 某某服务

  3. TestNG系列之三:TestNG忽略测试

    有时,我们的代码是没有准备好,如果测试用例写入到测试方法/代码将无法运行,在这种情况下, @Test(enabled = false)有助于禁用此测试案例.

  4. 利用HTML5与jQuery技术创建一个简单的自动表单完成

    来源:GBin1.com 在线演示   在线下载 谷歌快速搜索自带大量自动完成插件——库中甚至还有一个附带的jQuery UI共享选项.然而今天我要寻找一个替代的解决方案.由DevBridge开发的j ...

  5. Android系统常用的权限

    开机自动允许  android.permission.RECEIVE_BOOT_COMPLETED,允许程序开机自动运行. 电量统计  android.permission.BATTERY_STATS ...

  6. ssm整合(Spring+SpringMVC+Mybatis)

    一.Spring Spring致力于提供一种方法管理你的业务对象.IOC容器,它可以装载bean(也就是我们java中的类,当然也包括service dao里面的),有了这个机制,我们就不用在每次使用 ...

  7. Js、JQuery脚本兼容

    1.获取属性值 IE:$("#xxx").prop("title"); chrome:$("#xxx").attr("title& ...

  8. ffmpeg与TS

    http://blog.csdn.net/shuyong1999/article/details/7176329 一个不错的音视频博客 0. 简介 FFmpeg是一个集录制.转换.音/视频编码解码功能 ...

  9. websphere中的会话超时设置 和 web应用中web.xml中session-timeout关系

    Tomcat默认的会话的超时时间设置 设置Tomcat session有效期的三种方式有: 1.在tomcat/conf/web.xml中修改session-timeout的值,该设置是TOMCAT全 ...

  10. jsp实现仿QQ空间新建多个相冊名称,向相冊中加入照片

    工具:Eclipse,Oracle,smartupload.jar.语言:jsp,Java:数据存储:Oracle. 实现功能介绍: 主要是新建相冊,能够建多个相冊,在相冊中加入多张照片,删除照片,删 ...