【问题标题】:Chain Promises in Play Framework Using Java使用 Java 实现 Play 框架中的链式 Promise
【发布时间】:2015-04-14 00:31:09
【问题描述】:

我有一个控制器操作,我需要调用第 3 方网络服务。

我的问题是我没有调用一项网络服务。我需要链接 4 到 5 个 Web 服务。我调用的每个 Web 服务都返回一个我需要处理的 JSON 对象,并且基于某些逻辑,我决定调用另一个 Web 服务(来自 4 个 Web 服务)或向调用者返回响应。这是我想要做的:

 public static Promise<Result> accounts(){
    return WS.url("url1").get().map(response1 -> {
        JsonNode mynode = response1.asJson();

        if (mynode.get("status").asInt()==200){
            Promise<JsonNode> jsonPromise = WS.url("url2").get().map(response2->{
                    return response2.asJson();
            });
        }

        return ok(mynode);
        }); 
}

现在,从文档来看,我认为我需要的是在每个 Web 服务调用都是一个 Promise 的地方链接 Promise。但我不知道该怎么做?

谢谢

【问题讨论】:

    标签: java playframework reactive-programming


    【解决方案1】:

    他们称它为 Reactive Composition,它就是这样完成的 ->

    public static Promise<Result> reactiveCombo() {
        Promise<WSResponse> promise1 = WS.url("url1").get();
        Promise<WSResponse> promise2 = WS.url("url2").get();
        Promise<WSResponse> promise3 = WS.url("url3").get();
    
        return promise1.flatMap(response1 -> {
            final JsonNode json1 = response1.asJson();
            if (!json1.has("someField")) {
                return Promise.promise(() -> badRequest());
            }
            return promise2.flatMap(response2 -> {
                final JsonNode json2 = response2.asJson();
                if (json1.get("someField").asText().equals(json2.get("someField").asText())) {
                    return Promise.promise(() -> badRequest());
                }
                return promise3.map(response3 -> {
                    final JsonNode json3 = response3.asJson();
                    if (json3.get("boolField").asBoolean()) {
                        return badRequest();
                    }
                    return ok();
                });
            });
        });
    }
    

    对于大量调用,您可以使用Promise.sequence() 并获得“创意”:

    private static Promise<JsonNode> getPromise(String url, Predicate<JsonNode> predicate) {
        return WS.url(url).get().map(response -> {
            JsonNode json = response.asJson();           
            if (predicate.negate().test(json)) {
                throw new Exception("BUMMER!");
            }
            return json;
        });
    }
    
    public static Promise<Result> reactiveCombo(List<String> urls) {
        List<Promise<JsonNode>> promises = new ArrayList<Promise<JsonNode>>(urls.size());
        Predicate<String> predURL = p -> p.contains("goodApi");
        Predicate<JsonNode> pred1 = p -> p.has("boolField") && p.get("boolField").asBoolean();
        Predicate<JsonNode> pred2 = p -> p.has("someField");
    
        urls.forEach(url -> {
            Promise<JsonNode> promise = predURL.test(url) ? getPromise(url, pred1) : getPromise(url, pred2);           
            promises.add(promise);
        });
    
        return Promise.sequence(promises).map(results -> ok()).recover(t -> badRequest());
    }
    

    其他细节+纪录片:

    Go Reactive with Java 8 & Play Framework(旧但仍有教育意义)

    JavaWS

    【讨论】:

    • 好的,这就像一个魅力。但是,代码看起来很难看。我正在与数据库服务器交谈以执行一些 CRUD 操作。我很清楚,可以对 DB REST API 进行大量改进,以减少调用次数并提供安全的事务操作。但这不是我能做的。例如,在一种情况下,我最终调用了 10 个 REST API。所以任何建议我可以如何改进代码
    • 好吧,例如,您可以将 URL 放入一个数组中,对其进行迭代,然后为每个 URL 创建一个 Promise&lt;T&gt;,并将其添加到 List&lt;Promise&lt;T&gt;&gt; promisesList。然后你可以使用Promise.sequence(primisesList).map((List&lt;T&gt; resultList) -&gt; {...});。这会大大减少嵌套。
    • 这可能会有所帮助。我希望将来维护此代码的人不知道我住在哪里:)
    • 嘿,来吧......这还不错。
    【解决方案2】:

    对于任意数量的 Promise,您可以使用递归来扁平化此结构。 首先创建一个类:

    public static class ChainedWebService {
        public final Optional<ChainedWebService> next;
    
        public final WSRequestHolder wsResponsePromise;
        private final F.Predicate<JsonNode> predicate;
    
        public ChainedWebService(String url, Optional<ChainedWebService> next, F.Predicate<JsonNode> predicate) {
            this.next = next;
            this.wsResponsePromise = WS.url(url);
            this.predicate = predicate;
        }
    
        public F.Promise<Result> processChain() {
            return wsResponsePromise.get().flatMap(new F.Function<WSResponse, F.Promise<Result>>() {
                @Override
                public F.Promise<Result> apply(WSResponse wsResponse) throws Throwable {
                    if (!predicate.test(wsResponse.asJson())) {
                        return F.Promise.pure(badRequest());
                    }
    
                    if (!next.isPresent()) {
                        return F.Promise.pure(ok());
                    }
    
                    return next.get().processChain();
                }
            });
        }
    }
    

    然后使用它:

    public static F.Promise<Result> reactiveCombo() {
        ChainedWebService chainedWebService3 = new ChainedWebService(
                "url3",
                Optional.<ChainedWebService>empty(),
                jsonNode -> jsonNode.get("boolField").asBoolean()
        );
    
        ChainedWebService chainedWebService2 = new ChainedWebService(
                "url2",
                Optional.of(chainedWebService3),
                jsonNode -> jsonNode.get("someField").asText().equals(jsonNode.get("someField").asText()));
    
        ChainedWebService chainedWebService1 = new ChainedWebService(
                "url1",
                Optional.of(chainedWebService2),
                jsonNode -> jsonNode.has("someField")
        );
    
        return chainedWebService1.processChain();
    }
    

    您可以实现一些基本的构建器,使构建过程更加合理。

    希望对你有帮助!

    【讨论】:

    • 有趣的方法,但你有Promise.sequence() 正是为此。另外,你真的冒着用这个解决方案填满堆栈的风险。
    • @sebster 使用Promise.sequence() 将意味着执行所有请求,在这种情况下这不是必需的。
    猜你喜欢
    • 2016-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多