玩框架并行 WSClient 调用错误管理

play framework parallel WSClient calls error management

我有一个操作,其中我进行了三个并行的 HTTP 调用(对其他服务),然后我将响应的内容合并到一个文档中,最后将其发送回客户端。 这是代码的工作示例:

@Inject
WSClient wsc;

public CompletionStage<Result> getUrlData() throws Exception {

    List<CompletionStage<WSResponse>> stages = new ArrayList<>();
    stages.add(wsc.url("http://jsonplaceholder.typicode.com/posts/1").get());
    stages.add(wsc.url("http://jsonplaceholder.typicode.com/posts/2").get());
    stages.add(wsc.url("http://jsonplaceholder.typicode.com/posts/3").get());

    return Futures
            .sequence(stages)
            .thenApply(responses -> {
                StringBuilder builder = new StringBuilder("[");
                responses.stream().forEach(response -> builder.append(response.getBody()).append(","));
                builder.deleteCharAt(builder.length()-1).append("]");
                return ok(builder.toString());
            })
            .exceptionally(ex -> ok("{\"error\": \"An error has occurred\"}"));

如果其中一项服务不可用(您可以模拟此行为,将其中一个 URL 的域名修改为不存在的域名),页面 returned 仅包含异常中包含的消息() 部分,而我需要 return 正确调用的内容加上未成功调用的错误消息。关于如何操作的任何提示?

我正在使用 Play 2.5.1。

谢谢, 安德里亚

基本上您只想为每个调用单独处理 .exceptionally(..)。这样的事情应该有效:

  1. 为个人 URL 创建一个 returns CompletionStage 的函数,合并您的错误处理(返回 JSON 的错误)
  2. 将其转换为要传递给 Futures.sequence
  3. 的完成阶段列表

顺便说一句,您可以使用 Jackson 的 ObjectMapper.createObjectNode()ObjectMapper.createArrayNode():

以编程方式构建对象,从而使 JSON 操作更好一些
private static final ObjectMapper mapper = new ObjectMapper();

private CompletionStage<JsonNode> getDataFromUrl(String url) {
    return wsc.url(url)
            .get()
            .thenApply(WSResponse::asJson)
            .exceptionally(ex -> {
                ObjectNode error = mapper.createObjectNode();
                error.put("error", ex.getMessage());
                return error;
            });
}

public CompletionStage<Result> getUrlData() throws Exception {

    List<String> urls = new ArrayList<>();
    urls.add("http://jsonplaceholder.typicode.com/posts/1");
    urls.add("http://jsonplaceholder.typicode.com/posts/2");
    urls.add("http://jsonplaceholder.typicode.com/posts/3");

    // Convert to a list of promises
    List<CompletionStage<JsonNode>> stages = urls
            .stream()
            .map(this::getDataFromUrl)
            .collect(Collectors.toList());

    return Futures
            .sequence(stages)
            .thenApply(responses -> {
                ArrayNode arrayNode = mapper.createArrayNode();
                responses.stream().forEach(arrayNode::add);
                return ok(arrayNode);
            });
}