使用 completableFuture 从 doPOST 写入 doGET 响应

Use completableFuture to write doGET response from doPOST

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

我的控制流程-

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

为了解决这个问题,我发现我可以使用一些异步编程机制,比如 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
}

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

您可能有请求 ID 与对象的映射,作为 class 的 属性 公开了两种方法:

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

... 其中 class HttpRequestResponse 是请求(您在 doGET 上收到)和响应(将由 [=16 提供)的简单包装=]:

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 也可以变成异步的,return 执行 id(然后客户端可以监听结果)。