1.简介

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

关于这个主题的解释已经很多次了,但是对于Spring框架提供的利用该功能的类似乎有些困惑。 我说的是从@Controller返回Callable和DeferredResult。

在本文中,我将实现两个示例,以显示其差异。

此处显示的所有示例均包含实现一个控制器,该控制器将执行长时间运行的任务,然后将结果返回给客户端。 长时间运行的任务由TaskService处理:

@Service
public class TaskServiceImpl implements TaskService {private final Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic 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);}
}

所有这些示例的源代码都可以在Github Spring-Rest仓库中找到 。

2.从阻塞控制器开始

在此示例中,请求到达控制器。 只有执行了长时间运行的方法并且退出@RequestMapping带注释的方法,该servlet线程才会被释放。

@RestController
public class BlockingController {private final Logger logger = LoggerFactory.getLogger(this.getClass());private final TaskService taskService;@Autowiredpublic 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上运行此示例,查看日志,可以看到直到处理了长时间运行的任务(5秒后)后,才释放servlet请求:

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,而不是直接返回结果:

@RestController
public class AsyncCallableController {private final Logger logger = LoggerFactory.getLogger(this.getClass());private final TaskService taskService;@Autowiredpublic 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将在另一个线程中调用Callable中定义的任务。 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;@Autowiredpublic 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;}

那么,与Callable有什么区别? 所不同的是这次线程是由我们管理的。 在不同的线程中设置DeferredResult的结果是我们的责任。

在此示例中,我们要做的是使用CompletableFuture创建一个异步任务。 这将创建一个新线程,将在其中执行长时间运行的任务。 在此线程中,我们将设置结果。

我们从哪个池中检索这个新线程? 默认情况下,CompletableFuture中的supplyAsync方法将在ForkJoin池中运行任务。 如果要使用其他线程池,可以将执行程序传递给supplyAsync方法:

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

如果运行此示例,我们将得到与Callable相同的结果:

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做同样的事情,即释放容器线程并在另一个线程中异步处理长时间运行的任务。 区别在于谁管理执行任务的线程。

我正在Google Plus和Twitter上发布我的新帖子。 如果您要更新新内容,请关注我。

翻译自: https://www.javacodegeeks.com/2015/07/understanding-callable-and-spring-deferredresult.html

了解Callable和Spring DeferredResult相关推荐

  1. Spring Boot使用Spring DeferredResult实现长轮询,纵享新丝滑让你体验丝滑般的感觉 - 第414篇

    相关历史文章(阅读本文前,您可能需要先看下之前的系列

  2. SpringBoot异步调用方法

    SpringBoot异步调用方法 一.spring boot--使用异步请求,提高系统的吞吐量 https://blog.csdn.net/liuchuanhong1/article/details/ ...

  3. 《Spring 5 官方文档》18. Web MVC 框架(五)

    自定义WebDataBinder初始化 要通过Spring定制与PropertyEditor的请求参数绑定 WebDataBinder,可以使用@InitBinder控制器中的-annotated @ ...

  4. Spring MVC 异步处理请求,提高程序性能

    原文:http://blog.csdn.net/he90227/article/details/52262163 什么是异步模式 如何在Spring MVC中使用异步提高性能?一个普通 Servlet ...

  5. 7、Spring MVC 之 处理异步请求

    Spring MVC 3.2开始引入Servlet 3中的基于异步的处理request.往常是返回一个值,而现在是一个Controller方法可以返回一个java.util.concurrent.Ca ...

  6. spring注解驱动开发-10 Servlet3.0

    Spring AOP实现 前言 servlet3.0简介 ServletContainerInitializer shared libraries(共享库) / runtimes pluggabili ...

  7. 国内最全的Spring Boot系列之五

    ​历史文章(累计400多篇文章) <国内最全的Spring Boot系列之一> <国内最全的Spring Boot系列之二> <国内最全的Spring Boot系列之三& ...

  8. Java笔记二十四——Spring开发

    Spring是一个支持快速开发Java EE应用程序的框架.它提供了一系列底层容器和基础设施,并可以和大量常用的开源框架无缝集成,可以说是开发Java EE应用程序的必备. 在Spring Frame ...

  9. Spring MVC中文文档翻译发布

    2019独角兽企业重金招聘Python工程师标准>>> 21.1 Spring Web MVC框架简介 Spring的模型-视图-控制器(MVC)框架是围绕一个DispatcherS ...

最新文章

  1. 全排列 leetcode java_LeetCode--046--全排列(java)
  2. (网络编程)URL下载网络资源
  3. QT的QAxFactory类的使用
  4. .NET Core 1.0.1 发布了
  5. vue 历史更新 功能
  6. Linux 运维人最常用 150 个命令汇总
  7. html5新增表单控件和表单属性
  8. java 中的事物怎么配置_java – 在hibernate中如何以编程方式设置事务的隔离级别,或者如何创建具有不同隔离级别的两个事务...
  9. python语言中函数在调用前必须先定义吗_应该在python中使用函数之前进行定义?...
  10. HtmlTextWriter类的学习
  11. 关于Navicat Premium 12注册机被windows病毒防护自动删除的问题解决
  12. MySQL数据库主从双向同步
  13. 无法退休的董小姐?董明珠继任格力董事长
  14. 第一章节:期货及衍生品概述
  15. 02点餐项目需求分析
  16. 【PAT A1094】The Largest Generation
  17. 《钢铁是怎样炼成的》读后感集合「15篇」
  18. Arduino IDE+_Attiny13/85实践(一) IED环境配置
  19. 强生单剂新冠疫苗对“德尔塔”有效;赛诺菲巴斯德将每年投资4亿欧元建mRNA疫苗中心 | 美通社头条...
  20. ROS 2行动-actions-

热门文章

  1. 利用老毛头启动盘重装win7
  2. sh(Spring+Spring mvc+hibernate)——BaseDao.java
  3. ssh(Spring+Spring mvc+hibernate)简单增删改查案例
  4. 谁说表单只能发get和post请求了?
  5. android 监听安装来源_Flutter插件开发之APK自动安装
  6. 为什么光标停在表格中间_word里面为什么打出来的数字中间为啥差一个光标的距离 - 卡饭网...
  7. 如何获得即时编译器(JIT)的汇编代码(linux环境下)
  8. java动态代理和cglib动态代理
  9. java cxf_拥抱模块化Java平台:Java 10上的Apache CXF
  10. java设计模式教程_Java设计模式教程