【问题标题】:Why aren't my callback methods working properly?为什么我的回调方法不能正常工作?
【发布时间】:2016-07-16 09:02:26
【问题描述】:

我使用 AsyncRestTemplate 来异步制作 resttemplate。

这些方法应该等待所有 asyncresttemplate 进程完成,并且它会返回 reviewContent。

问题是回调方法不起作用,在整个方法完成之前。所以我不能正确返回 optionName 和membershipGradeCode 的值,reviewType 应该包含在reviewContent 中。

有人能解释一下我现在缺少什么吗?

rev#1 成功回调方法改变了reviewContent的状态,会不会有问题?

public ReviewContent getRepresentativeReviewContent(Long dealNo, Long categoryNo, String setId) {

    Optional<Map<String, Object>> review = Optional.ofNullable(boardApi.getRepresentativeReviewContent(dealNo));

    if (review.isPresent()) {
        Long memberNo = Long.valueOf(review.get().get("memberNo").toString());
        ReviewContent reviewContent  = new ReviewContent();

        ListenableFuture<ResponseEntity<Map>> optionInfo = dealApi.asyncGetDealOption(Long.valueOf(review.get().get("optionNo").toString()));
        optionInfo.addCallback(success -> {
            try {
                reviewContent.setOptionName((String) ((Map<String, Object>) success.getBody().get("data")).get("dealTitle"));
            } catch (Exception e) {
                reviewContent.setOptionName(null);
            }
        }, failure -> LOGGER.error("asyncGetDealOption", failure.getStackTrace()));

        ListenableFuture<ResponseEntity<Map>> gradeInfoOfThisMember = mktApi.asyncGetMembershipGradeOfThisMember(memberNo);
        gradeInfoOfThisMember.addCallback(success -> {
                    try {
                        reviewContent.setMembershipGradeCode((Integer) ((Map<String, Object>) success.getBody().get("data")).get("grade"));
                    } catch (Exception e) {
                        reviewContent.setMembershipGradeCode(0);
                    }
                        },
                        failure -> {
                            reviewContent.setMembershipGradeCode(0);
                            LOGGER.error("asyncGetMembershipGradeOfThisMember", failure.getStackTrace());
                        });

        ListenableFuture<ResponseEntity<ReviewType>> reviewTypeByCategoryNo = boardApi.asyncGetReviewTypeByCategoryNo(categoryNo, setId);
        reviewTypeByCategoryNo.addCallback(success -> {
                    try {
                        reviewContent.setReviewType(success.getBody());
                    } catch (Exception e) {
                        reviewContent.setReviewType(null);
                    }
                },
                failure -> {
                    reviewContent.setReviewType(null);
                    LOGGER.error("asyncGetReviewTypeByCategoryNo", failure.getStackTrace());
                });

        reviewContent.setReviewCount((Integer) review.get().get("reviewCount"));
        reviewContent.setReviewAvgScore((Double) review.get().get("reviewAvgScore"));
        reviewContent.setContents((String) review.get().get("contents"));
        reviewContent.setCreateDt((String) review.get().get("createdDt"));
        reviewContent.setUpdateDt((String) review.get().get("updatedDt"));
        reviewContent.setMemberSrl(memberNo);
        reviewContent.setTitle((String) review.get().get("title"));
        reviewContent.setAccountSrl(Long.valueOf(review.get().get("accountNo").toString()));
        reviewContent.setMemberId((String) review.get().get("memberId"));
        reviewContent.setAccountSrl(Long.valueOf(review.get().get("accountNo").toString()));

        boolean isApiExecutionDone = false;
        while (!isApiExecutionDone) {
            if (gradeInfoOfThisMember.isDone() && optionInfo.isDone() && reviewTypeByCategoryNo.isDone()) {
                isApiExecutionDone = true;
            }
        }

        return reviewContent;
    }

    return new ReviewContent();
}

【问题讨论】:

  • SO 不适合为您调试代码。你甚至不说什么是行不通的。您是否尝试过将问题简化为更简单的测试用例?实际上,您尝试过什么?
  • @FrankPavageau 抱歉,没有测试用例。问题是回调方法不起作用。由于等待三个 AsyncRestTemplate 结果,我在方法结束时做了 while 语句。 dealApi.asyncGetDealOption, mktApi.asyncGetMembershipGradeOfThisMember(memberNo); gradeInfoOfThisMember,boardApi.asyncGetReviewTypeByCategoryNo
  • 首先,ListenableFuture 返回的 AsyncRestTemplate 是一个 Spring 类,而不是 Guava 类。使用您的调试器,在第一个异步调用和各种回调中设置断点,然后进入方法以找出任务在哪个线程或线程池上提交,查看使用的队列等。问题仅在于回调,或者Futures 永远不会完成?
  • @FrankPavageau 我已经调试过了,但我无法解决这个问题。我认为回调正在工作,但在回调方法工作之前返回了 reviewContent。当我调试时,如果我停在“.isDone”点并等待几秒钟,reviewContent 会正确返回。我不知道这是什么......线程有问题吗?还是 isDone 有问题??
  • '无法正常工作'不是问题描述。愚蠢的标题。投反对票。

标签: java asynchronous callback resttemplate spring-web


【解决方案1】:

所以您的问题是回调在您的方法返回的对象上设置了属性。但是,它们异步执行,并且不属于Futuredone 状态:一旦Future 完成,它们本身就会与@ 中的代码同时执行987654325@ 方法。由于该方法会在所有 Futures 完成后立即返回,因此没有(全部)按应有的方式设置属性。

此外,您没有显示ReviewContent 对象的代码,但我很确定它没有将optionTypemembershipGradeCodereviewType 字段声明为volatile。由于方法中没有障碍(例如synchronized 块或Locks),Java 内存模型中不能保证回调(即其他线程中)设置的值会在线程执行中看到getRepresentativeReviewContent 方法。

回调应该只用于主执行路径之外的副作用,因为很难与它们协调:你必须使用像 CountDownLatch 这样的东西来确保它们已经全部执行,这将使代码更复杂。

直接等待异步结果(代码未经测试):

try {
    // Not sure why you need to catch Exception here?
    // If it's for flow control (absent entry in a Map), it's a bad practice.
    // Just check it instead of catching NullPointerException.
    reviewContent.setOptionName((String) 
            ((Map<String, Object>) optionInfo.get().getBody().get("data"))
                    .get("dealTitle"));
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    LOGGER.error("asyncGetDealOption", e);
    reviewContent.setOptionName(null);
} catch (CancellationException | ExecutionException e) {
    LOGGER.error("asyncGetDealOption", e);
    reviewContent.setOptionName(null);
}

另一种选择是编写Futures,例如可以使用 Guava 的Futures.transform 来实际从完整响应中获取您需要的字符串,因此您只需调用 get() 即可。 987654340@ 设置您的财产。不过,您仍然必须管理这些错误。

【讨论】:

  • 真的很有帮助,谢谢弗兰克!如果我制作回调方法,我会考虑主路径之外的副作用。太好了!
猜你喜欢
  • 2013-05-21
  • 1970-01-01
  • 2020-03-28
  • 1970-01-01
  • 2021-09-01
  • 1970-01-01
  • 2016-01-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多