CompletableFuture:如何将两个异步请求合并为一个响应

CompletableFuture: how to combine two asynchronous requests into one response

第一个请求是异步发送的,然后从对第一个请求的响应中取出id值,用在第二个异步请求中。然后合并对请求 1 和请求 2 的响应。下面的示例有效,但不是异步的,因为使用了 .get() 。有没有办法异步执行此操作? 流程简而言之 - 一切都应该异步发生:

  1. 发送POST请求1
  2. 将响应 1 中的 ID 值用于请求 2
  3. 发送POST请求2
  4. 将响应1和响应2合并为REST控制器的最终响应

此主体通过 POST 方法发送到 REST 控制器端点“/combine”:

{ "userid": 1, "id": 2, "title": "3", "body": "4" }

@RestController
public class CombinationController {
    @Autowired
    CombinationService combinationService;

    @PostMapping("/combine")
    public CompletableFuture<CombinationBothResponses> combine(@RequestBody RequestBodyOne requestBodyOne) {
        return combinationService.combine(requestBodyOne);
    }
}
@Service
public class CombinationService {
    private final Jsonb jsonb = JsonbBuilder.create();
    private final HttpClient httpClient = HttpClient.newBuilder().build();

    public CompletableFuture<CombinationBothResponses> combine(RequestBodyOne requestBodyOne) {
        // 1. Send POST request 1
        HttpRequest httpRequestOne =
                HttpRequest.newBuilder(URI.create("https://jsonplaceholder.typicode.com/posts"))
                        .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .POST(HttpRequest.BodyPublishers.ofString(jsonb.toJson(requestBodyOne)))
                        .build();

        return httpClient
                .sendAsync(httpRequestOne, HttpResponse.BodyHandlers.ofString())
                .thenApply(
                        httpResponse -> {
                            if (HttpStatus.valueOf(httpResponse.statusCode()).is2xxSuccessful()) {
                                CombinationBothResponses combinationBothResponses =
                                        jsonb.fromJson(httpResponse.body(), CombinationBothResponses.class);
                                // 2. Use one value from response 1 for request 2
                                int valueToBeUsedInRequestBody2 = combinationBothResponses.getId();
                                // 3. Send POST request 2
                                CompletableFuture<CombinationBothResponses> completableFuture2 =
                                        sendSecondPostRequest(valueToBeUsedInRequestBody2);
                                // 4. Combine response 1 and response 2 to the final response of REST controller
                                try {
                                    CombinationBothResponses responseBodyRequestTwo =
                                            completableFuture2.get(); // Not asynchronous
                                    combinationBothResponses.setSuccess(responseBodyRequestTwo.getSuccess());
                                    return combinationBothResponses;

                                } catch (InterruptedException | ExecutionException e) {
                                    e.printStackTrace();
                                }
                            }
                            throw new RuntimeException();
                        });
    }

    private CompletableFuture<CombinationBothResponses> sendSecondPostRequest(
            int valueToBeUsedInRequestBody2) {
        RequestBodyTwo requestBodyTwo = new RequestBodyTwo(valueToBeUsedInRequestBody2, "request 2");
        HttpRequest httpRequest =
                HttpRequest.newBuilder(URI.create("https://reqbin.com/echo/post/json"))
                        .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                        .POST(HttpRequest.BodyPublishers.ofString(jsonb.toJson(requestBodyTwo)))
                        .build();

        return httpClient
                .sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString())
                .thenApply(
                        httpResponse -> {
                            if (HttpStatus.valueOf(httpResponse.statusCode()).is2xxSuccessful()) {
                                CombinationBothResponses responseBodyRequestTwo =
                                        jsonb.fromJson(httpResponse.body(), CombinationBothResponses.class);
                                return responseBodyRequestTwo;
                            }
                            throw new RuntimeException();
                        });
    }
}
@Data
@NoArgsConstructor
public class RequestBodyOne {
    private int userId;
    private int id;
    private String title;
    private String body;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RequestBodyTwo {
    private int id;
    private String key;
}
@Data
@NoArgsConstructor
public class CombinationBothResponses {
    private int userId;
    private int id;
    private String title;
    private String body;

    private String success;

}

对请求 1 的响应:

{ "userId": 0, "id": 101, "title": "3", "body": "4" }

对请求 2 的响应:

{"success":"true"}

综合回复; REST 控制器的响应:

{ "userId": 0, "id": 101, "title": "3", "body": "4", "success": "true" }

return httpClient
     .sendAsync(httpRequestOne, HttpResponse.BodyHandlers.ofString())
     .thenCompose(httpResponse -> {
         if (HttpStatus.valueOf(httpResponse.statusCode()).is2xxSuccessful()) {
             final CombinationBothResponses combinationBothResponses = jsonb.fromJson(httpResponse.body(), CombinationBothResponses.class);
             // 2. Use one value from response 1 for request 2
             int valueToBeUsedInRequestBody2 = combinationBothResponses.getId();
             // 3. Send POST request 2
             return sendSecondPostRequest(valueToBeUsedInRequestBody2)
                 .thenApply(responseBodyRequestTwo -> {
                     // 4. Combine response 1 and response 2 to the final response of REST controller
                     combinationBothResponses.setSuccess(responseBodyRequestTwo.getSuccess());
                     return combinationBothResponses;
                 });
         }
         return CompletableFuture.failedFuture(new RuntimeException());
     });