将CompletableFuture与@Async一起使用会为spring boot API返回一个空响应

时间:2017-07-06 00:17:41

标签: java spring asynchronous java-8 completable-future

这是我的控制器。我用邮递员测试它是否正常工作,但我得到一个空洞的回应。我在应用程序配置中使用了@EnableAsync,在服务上使用了@Async。如果我在服务层上删除@Async它可以工作,但它不会异步运行。

@ApiOperation(value = "search person by passing search criteria event/title/role/host/is_current", response = ElasticSearchResultData.class)
@RequestMapping(value = "/async2/searchPerson", produces = "application/json", method = RequestMethod.POST)
public @ResponseBody CompletableFuture<ElasticSearchResultData> searchPersonAsync2(@RequestBody SearchCriteriaTo criteriaForDivNetFolderTo,
        HttpServletRequest request, HttpServletResponse response){
    LOGGER.info("searchPerson controller start");
    SearchCriteria searchCriteria = criteriaForDivNetFolderTo.getSearchCriteria();
    if (Util.isNull(searchCriteria)) 
        throw new IllegalArgumentException("search criteria should not be null.");

    try {
        CompletableFuture<ElasticSearchResultData> searchPerson = cubService.searchPersonAsync2(criteriaForDivNetFolderTo);
        ObjectMapper mapper = new ObjectMapper();
        LOGGER.info("search Person "+mapper.writeValueAsString(searchPerson));
        return searchPerson;
    } catch (Exception e) {
        LOGGER.error("Exception in searchPersonAsync controller "+e.getMessage());
    }
    return null;
}

服务

@Async
@Override
public CompletableFuture<ElasticSearchResultData> searchPersonAsync2(SearchCriteriaTo searchCriteriaForDivNetFolderTo) {
   Long start = System.currentTimeMillis();
   LOGGER.info(":in searchPerson");
   CompletableFuture<ElasticSearchResultData> completableFuture = new CompletableFuture<>();
   ElasticSearchResultData searchResultData = null;
   SearchCriteria searchCriteria = searchCriteriaForDivNetFolderTo.getSearchCriteria();
   try {
        LOGGER.info("************ Started searchPerson by criteria ************");
        StringBuilder url = new StringBuilder();
        url.append(equilarSearchEngineApiUrl)
        .append(focusCompanySearchUrl)
        .append("/")
        .append("searchPerson")
        .append("?view=").append(VIEW_ALL)
        .append("&isProcessing=true");

        LOGGER.debug("Calling equilar search engine for focused company search, URL : " + url);
        LOGGER.info(searchCriteria.toString());
        String output = null;
        if (redisEnable != null && redisEnable) {
            output = cacheDao.getDataFromRestApi(url.toString(), RequestMethod.POST.name(), searchCriteria);
        } else {
            output = Util.getDataFromRestApi(url.toString(), RequestMethod.POST.name(), searchCriteria);
        }
        if (!Util.isEmptyString(output)) {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            searchResultData = objectMapper.readValue(output,
                            objectMapper.getTypeFactory().constructType(ElasticSearchResultData.class));
        }
        List<PersonSearchDetails> newPersonDetails = new ArrayList<PersonSearchDetails>();
        if (!Util.isNull(searchResultData) && !Util.isNullOrEmptyCollection(searchResultData.getPersonDetails())
                && !Util.isNullOrEmptyCollection(searchCriteriaForDivNetFolderTo.getNetworkFoldersData())) {
            for (PersonSearchDetails personDetail : searchResultData.getPersonDetails()) {
                String logoUrl = null;
                if(!Util.isNull(searchCriteria.getTargetFolderId())){
                    List<DiversityNetworkFolderTo> filteredFolderTos = searchCriteriaForDivNetFolderTo
                            .getNetworkFoldersData()
                            .stream()
                            .filter(folder -> folder.getId()
                            .longValue() == searchCriteria
                            .getTargetFolderId())
                            .collect(Collectors.toList());
                    logoUrl = getLogoUrl(personDetail.getPersonId(),
                            filteredFolderTos);
                } else {
                    logoUrl = getLogoUrl(personDetail.getPersonId(),
                            searchCriteriaForDivNetFolderTo.getNetworkFoldersData());
                }
                personDetail.setLogoUrl(logoUrl);
                newPersonDetails.add(personDetail);
            }
            searchResultData.setPersonDetails(newPersonDetails);
        }
        completableFuture.complete(searchResultData);
        return completableFuture;
    } catch (Exception e) {
        completableFuture.completeExceptionally(e);
        LOGGER.error(
                " ************** Error in proccessing searchPerson by criteria ************** " + e.getMessage());
    }
    Long end = System.currentTimeMillis();
    LOGGER.info(TIME_DURATION+(end - start)+"ms");
    return null;
}

1 个答案:

答案 0 :(得分:0)

阅读有关 async 处理的更多信息会更好。 javadocs通常是一个很好的开始!

如果您真的想从 Future 方法获得结果,则需要等待它。

CompletableFuture API中有一个方法public T get()等待等待创建结果,并在结果完成后返回结果。

如果您的工作是在数据库中搜索结果然后返回它 - 您仍然需要等待它 async 在这里没什么帮助。如果你不得不同时制作多件东西,它会对你有所帮助,例如:同时调用DB,Web服务和其他东西,然后你可以创建一个期货数组并等待它们全部完成。

或者,假设您正在创建POST方法,因此您可以快速验证输入并发送到存储到DB async ,同时快速将响应返回到UI和希望您的异步方法将在另一个线程中完成,而不会将任何错误返回给UI。

当你知道自己在做什么的时候,这是一个很棒的技巧,但是想想&amp;当你真正需要它之前使用它。

“修复”这个问题的简短方法是:

CompletableFuture<ElasticSearchResultData> searchPerson = cubService.searchPersonAsync2(criteriaForDivNetFolderTo);
ElasticSearchResultData result = searchPerson.get();
ObjectMapper mapper = new ObjectMapper();
LOGGER.info("search Person "+mapper.writeValueAsString(result));
return result;

(显然改变方法返回签名)