【问题标题】:CompletableFuture thenCompose after exceptionallyCompletableFuture thenCompletable after 异常
【发布时间】:2023-11-22 07:01:01
【问题描述】:

我被 CompletableFuture 异常处理困住了

我的逻辑是发送电子邮件并保存此操作的状态。如果发送电子邮件引发异常,我需要使用异常消息保存状态。

public interface MyService {
CompletableFuture<Boolean> sendEmail(String content, String address);
CompletableFuture<StatusResult> saveStatus(String content, String address);}

处理器类目前有此代码。它工作正常,但对我来说并不优雅。我们如何摆脱用于在阶段之间共享状态的错误本地字段?

@Component
public class Processor {
    private static final Logger LOGGER = LoggerFactory.getLogger(Processor.class);
    @Autowired
    private MyService myService;

    public CompletableFuture<StatusResult> sendEmail(String content, String address) {
        AtomicReference<String> error = new AtomicReference<>();// just to forward error message from exception block to thenCompose
        return myService.sendEmail(content, address).exceptionally(e -> {
            LOGGER.error("Exception during send email ", e);
            error.set(e.getMessage());
            return null;
        }).thenCompose(x -> {
            if (x == null) {
                return myService.saveStatus(error.get(), address);
            } else {
                return myService.saveStatus("good", address);
            }
        });

    }
}

看起来 handle 方法应该有帮助,但它返回 CompletableFuture 的 CompletableFuture

 public CompletableFuture<StatusResult> sendEmail(String content, String address) {
    CompletableFuture<CompletableFuture<StatusResult>> result = myService.sendEmail(content, address).handle((x, e) -> {
        if (e != null) {
            LOGGER.error("Exception during send email ", e);
            return myService.saveStatus("error", address);
        } else {
            return myService.saveStatus("good", address);
        }
    });
}

【问题讨论】:

  • 你不需要两个 CompletableFuture。 sendEmail 已经在一个单独的线程中运行,让它等待 saveStatus 并返回结果。
  • 我根本无法阻塞,因为它适用于 Spring WebFlux。阻塞会影响所有请求

标签: java multithreading spring-webflux project-reactor completable-future


【解决方案1】:

另一个可行的解决方案:

public CompletableFuture<StatusResult> sendEmailAndSaveStatus(String content, String address) {
    CompletableFuture<Boolean> sendEmail = myService.sendEmail(content, address);
    CompletableFuture<StatusResult> result = new CompletableFuture<>();
    sendEmail.exceptionally(e -> {
        LOGGER.info("Exception during send email ");
        myService.saveStatus(e.getMessage(), address).thenApply(x -> result.complete(x));
        return false;
    });
    sendEmail.thenCompose(x -> myService.saveStatus("good", address)).thenApply(x -> result.complete(x));
    return result;
}

【讨论】:

  • 这不起作用,因为您将两个处理程序(完成阶段)链接到同一个CompletableFuture&lt;&gt; sendEmail - 其中一个会发生,另一个不会。此外,您可以使用thenApply 将一种转换为另一种类型,而不是创建第二个可完成的未来。
【解决方案2】:

您可以预先转换为您的保存状态。

public CompletableFuture<String> sendEmail(String content, String address) {

    return myService.sendEmail(content, address)
            .thenApply(b -> "good")
            .exceptionally(Throwable::getMessage)
            .thenCompose(status -> myService.saveStatus(status, address));
}

【讨论】:

  • 非常优雅。荣誉。