博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
DeferredResult实例一:理解Spring的Callable和DeferredResult
阅读量:6908 次
发布时间:2019-06-27

本文共 4794 字,大约阅读时间需要 15 分钟。

hot3.png

1-介绍

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

这个主题已经解释了很多次,Spring框架提供的关于这个功能的类似乎有一点混乱——在一个Controller中返回Callable 和 DeferredResult。
在这篇文章中,我将实施这两个例子,以显示其差异。
这里所显示的所有示例都包括执行一个控制器,该控制器将执行一个长期运行的任务,然后将结果返回给客户机。长时间运行的任务由taskservice处理:

@Servicepublic 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创建的,我们将执行下面的类来运行我们的例子:

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

 

2-阻塞的Controller

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

@RestControllerpublic 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秒后)。

2017-04-19 10:28:56,860 INFO (BlockingController.java:23)- Request received2017-04-19 10:29:01,861 INFO (TaskServiceImpl.java:16)- Slow task executed2017-04-19 10:29:01,863 INFO (BlockingController.java:25)- Servlet thread released

3-返回Callable

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

@RestControllerpublic 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
executeSlowTask() { logger.info("Request received"); Callable
callable = taskService::execute; logger.info("Servlet thread released"); return callable; }}

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

2017-04-19 10:34:35,554 INFO (AsyncCallableController.java:23)- Request received2017-04-19 10:34:35,559 INFO (AsyncCallableController.java:25)- Servlet thread released2017-04-19 10:34:40,585 INFO (TaskServiceImpl.java:16)- Slow task executed

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

 

4-返回DeferredResult

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

@RestControllerpublic 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
executeSlowTask() { logger.info("Request received"); DeferredResult
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  CompletableFuture supplyAsync(Supplier supplier, Executor executor)

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

2017-04-19 10:42:17,092 INFO (TaskServiceImpl.java:16)- Slow task executed2017-04-19 10:42:23,112 INFO (AsyncDeferredController.java:25)- Request received2017-04-19 10:42:23,114 INFO (AsyncDeferredController.java:29)- Servlet thread released

5-结论

站在一定高度来看这问题,Callable和Deferredresult做的是同样的事情——释放容器线程,在另一个线程上异步运行长时间的任务。不同的是谁管理执行任务的线程:Callable执行线程完毕即返回;Deferredresult通过设置返回对象值(deferredResult.setResult(result));)返回,可以在任何地方控制返回。

转载于:https://my.oschina.net/u/2935389/blog/2051592

你可能感兴趣的文章
我的友情链接
查看>>
继续探索其他不同类型Activity的属性,可以获取到所有Activity的属性
查看>>
Spring+Struts2+Hibernate整合
查看>>
C#常见问题总结(二)
查看>>
Linux系统基础-Linux系统文件操作常用命令
查看>>
网络工程师成长日记307-XX公安监控中心技术支持回忆录
查看>>
JavaWeb17-HTML篇笔记(二)
查看>>
vlan虚拟局域网
查看>>
简练软考知识点整理-估算成本过程
查看>>
Cobbler自动装机,Cobbler Web管理的两种认证方式登录
查看>>
部署社交网站(SVN+PHP+NGINX+MYSQL+MFS)
查看>>
PXE 网络装机——实现无人值守批量装机
查看>>
【爱项目、爱管理】项目成本管理如何做到又好又省钱
查看>>
云计算入门学习资料,linux云计算学习大纲
查看>>
大数据开发语言学习指南:新人快速学习大数据的套路
查看>>
好用的企业邮箱有哪些?
查看>>
Oracle热备份
查看>>
动态规划之经典例题
查看>>
gitlab web hooks 应用
查看>>
STM32的停机模式与唤醒
查看>>