【问题标题】:Use completableFuture to write doGET response from doPOST使用 completableFuture 编写来自 doPOST 的 doGET 响应
【发布时间】:2026-01-27 09:45:01
【问题描述】:

我有 2 个 API(doGET 和 doPOST),我正在尝试使用异步机制让 doPOST 为 doGET 请求写入 httpServletResponse

我的控制流程 -

  1. 客户端发出 requestA (getData) 调用
  2. Java 服务器进行一些处理并调用外部环境 3rd 方 API
  3. 第 3 方 API 不返回响应但调用我的另一个端点 doPOST
  4. doPOST 现在需要将 httpServletResponse 的对象写入 doGET
  5. doGET 在 doPOST 完成后立即返回此对象。

为了解决这个问题,我发现我可以在java中使用一些异步编程机制,比如CompletableFuture。但是我对如何在我的代码中准确设置这个机制感到困惑。这是我到目前为止所做的 -

doGET

public void doGET(HttpServletRequest request, HttpServletResponse response) {
    // some processing
    // Call 3rd Party API
    CompletableFuture<HttpServletRequest> completableFuture = CompletableFuture.supplyAsync(() -> doPOST());
    while (!completableFuture.isDone()) {
        System.out.println("CompletableFuture is not finished yet...");
    }
    HttpServletRequest result = completableFuture.get();
    
    response = result;
}

我无法弄清楚如何为此设置 completableFuture。在这里需要帮助。

doPOST

public HttpServletResponse doPOST(HttpServletRequest request, HttpServletResponse response) {
    // receive 3rd party request 
    // add data from 3rd party request into a new response object 
    // add response object into hashmap
}

我怎样才能正确地完成这项工作?

【问题讨论】:

  • 您需要 doPost 中 3rd Party Api 的响应吗?
  • 我不确定您是否真的想在循环中打印“CompletableFuture 尚未完成”,这将使 JVM 可能会打印数千次相同的消息而根本没有实用程序。相反,只需调用 .get() 它将等待 Future 完成,然后再继续执行。
  • 如果步骤的执行顺序相互依赖,那么使用可完成的未来没有好处。但是如果有一些步骤是不相互依赖的,你可以得到使用completable future的好处。
  • @Pirate 3rd 方 API 请求是一个 POST 请求。所以我只需要向该请求发送 200 OK,然后 doPOST 将执行 doGET 响应的写入。
  • 查看您的 doPost 是否依赖于第 3 方响应。这里的response不仅是响应体,还有api返回的状态码。在这种情况下,两个 api 相互依赖,因此您不会从使用 completable future 中获得任何好处。

标签: java asynchronous completable-future


【解决方案1】:

您可能有一个请求 id 与一个对象的映射作为您的类的属性,公开这两种方法:

private final Map<String, HttpRequestResponse> requests = new HashMap<>();

...其中HttpRequestResponse 类是请求(您在doGET 上收到)和响应(将由doPOST) 提供)的简单包装器:

class HttpRequestResponse {
    private final HttpServletRequest request;
    private final CompletableFuture<HttpServletResponse> responseSupplier;

    public HttpRequestResponse(HttpServletRequest request, CompletableFuture<HttpServletResponse> responseSupplier) {
        this.request = request;
        this.responseSupplier = responseSupplier;
    }

    public void supplyResponse(HttpServletResponse response) {
        this.responseSupplier.complete(response); //<-- this will release the .get()
    }

    //getters
    public CompletableFuture<HttpServletResponse> getSupplier() {
        return responseSupplier;
    }
}

doGET收到请求后,您将创建实例并将其放入地图中,然后等待结果:

public void doGET(HttpServletRequest request, HttpServletResponse response) {
    HttpRequestResponse responseSupplier = new HttpRequestResponse(request, new CompletableFuture<>());
    requests.put(yourId, responseSupplier); //add supplier to the map (so that doPOST can retrieve it later)
    //perform request to your 3rd party API
    response = responseSupplier.getSupplier().get(); //<- wait until someone completes the future
}

另一方面,在收到来自doPOST 的第 3 方 API 的响应后,您需要通过其 id 获取未来,将其从地图中移除并完成:

public void doPOST(HttpServletRequest request, HttpServletResponse response) {
    HttpRequestResponse responseSupplier = requests.remove(yourId); //<-- removes the supplier from the map and returns it to you
    responseSupplier.getSupplier().complete(<your response>); //<-- once you complete the future with a result, the .get() which is hanging on doGET will return
}

问题:如果doGET 在继续之前一直等待响应准备好,为什么要使用异步模式? 我想一旦你把它放在适当的位置,doGET 也可以变成异步的并返回执行 id(然后客户端可以监听结果)。

【讨论】:

  • 我正在尝试重现 AWS 为对象 lambda 执行的机制。这是我的个人项目。在 AWS 中,有一个称为 WriteGetObjectResponse() 的 API,它将响应写入 GetObject(),然后 getObject() 返回它。我想弄清楚他们是怎么做到的。
  • 我的第 3 方 API 是一个发布请求。所以我只需要向第 3 方 API 发送 200 OK 作为状态码,并为我的原始请求构建响应。我现在要试试你的解决方案。
  • 我认为您的答案就是我要找的答案。但我对 doPOST 部分有点困惑。 responseSupplier.getSupplier().complete(&lt;your response&gt;); 你提到了&lt;your-response&gt;。所以如果我愿意为doGET请求构建一个响应,我应该先构建一个响应对象,然后提供它来代替&lt;your-response&gt;吗?
  • @AniruddhaTekade 应该只包含您在之前的解决方案中使用的任何内容来为您的 doGET 编写响应
  • @a13e 这当然是可能的,但这是一个完全不同的问题。我建议您关闭此帖子并针对正确的问题打​​开一个新帖子,您将获得更多受众的更多关注,我们避免在不相关的帖子上展开过多讨论:)