javascript
了解Callable和Spring DeferredResult
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 released3.返回可致電
在此示例中,我們將直接返回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 executed5.結論
從高層次來看,Callable和DeferredResult做同樣的事情,即釋放容器線程并在另一個線程中異步處理長時間運行的任務。 區別在于誰管理執行任務的線程。
我正在Google Plus和Twitter上發布我的新帖子。 如果您要更新新內容,請關注我。
翻譯自: https://www.javacodegeeks.com/2015/07/understanding-callable-and-spring-deferredresult.html
總結
以上是生活随笔為你收集整理的了解Callable和Spring DeferredResult的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓开发手册中文版(安卓开发手册)
- 下一篇: 取消二类医疗器械备案的范围(取消二类医疗