背景

web请求的时候,有时候会遇到后台操作耗时特别久的操作,前端UI界面总是会请求超时。

举例

代码

  1. @Service
  2. public class AsyncTaskService {
  3. private Logger logger = LoggerFactory.getLogger(AsyncTaskService.class);
  4. public String uuid(){
  5. try{
  6. TimeUnit.MILLISECONDS.sleep(5000);
  7. logger.info("working thread");
  8. } catch (InterruptedException e) {
  9. logger.error("uuid error ", e);
  10. }
  11. return UUID.randomUUID().toString();
  12. }
  13. }
  1. @RestController
  2. public class AsyncController {
  3. @Autowired
  4. AsyncTaskService asyncTaskService;
  5. @GetMapping("blockingrequest")
  6. public String blockingRequest() {
  7. logger.info("blockingRequest: start");
  8. String uuid = asyncTaskService.uuid();
  9. logger.info("blockingRequest: end");
  10. return uuid;
  11. }
  12. }

结果:

5s后返回response。
image.png

目标

开一个子线程异步执行耗时任务。

解决方案

1. @Async

  1. public class AsyncTaskService {
  2. @Async
  3. public String getUuid() {
  4. try {
  5. TimeUnit.MILLISECONDS.sleep(5000);
  6. logger.info("working thread");
  7. } catch (InterruptedException e) {
  8. logger.error("getUuid error ", e);
  9. }
  10. return UUID.randomUUID().toString();
  11. }
  12. }
  1. @RestController
  2. public class AsyncController {
  3. @Autowired
  4. AsyncTaskService asyncTaskService;
  5. @GetMapping("asyncMethod")
  6. public String asyncMethod() {
  7. logger.info("asyncMethod: start");
  8. String uuid = asyncTaskService.getUuid();
  9. logger.info("asyncMethod: end");
  10. return uuid;
  11. }
  12. }

5s后返回response。
image.png

好像没什么用,待查。

2. Callable

  1. @RestController
  2. public class AsyncController {
  3. @Autowired
  4. AsyncTaskService asyncTaskService;
  5. @GetMapping("callable")
  6. public Callable<String> callable() {
  7. logger.info("callable: start");
  8. Callable<String> callable = () -> {
  9. String uuid = asyncTaskService.uuid();
  10. logger.info("child task thread");
  11. return uuid;
  12. };
  13. logger.info("callable: end");
  14. return callable;
  15. }
  16. }

5s后返回response。虽然先打印了”callable: end”,但是还是会等到线程返回结果后,再返回给UI。还是会超时。
image.png

3. DefferedResult

  1. @RestController
  2. public class AsyncController {
  3. @Autowired
  4. AsyncTaskService asyncTaskService;
  5. @GetMapping("deferredResultRequest")
  6. public DeferredResult<String> deferredResultRequest() {
  7. logger.info("deferredResultRequest: start");
  8. DeferredResult<String> deferredResult = new DeferredResult<>(2000l);
  9. //可使用线程池
  10. // ExecutorService executorService = Executors.newFixedThreadPool(10);
  11. // executorService.submit(() -> {
  12. // asyncTaskService.uuid(request, response);
  13. // deferredResult.setResult("watch:complete");
  14. // });
  15. new Thread(()-> {
  16. asyncTaskService.uuid();
  17. deferredResult.setResult("deferredResultRequest:complete");
  18. }).start();
  19. deferredResult.onTimeout(() -> deferredResult.setResult("deferredResultRequest:timeout"));
  20. deferredResult.onCompletion(()->deferredResult.setResult("deferredResultRequest:complete"));
  21. deferredResult.onError(throwable -> deferredResult.setErrorResult("deferredResultRequest:error"));
  22. logger.info("deferredResultRequest: return");
  23. return deferredResult;
  24. }
  25. }

返回结果:deferredResultRequest:timeout
可在设置的时间内返回结果给UI,不论子线程是否完成。
image.png