在不使用AsyncResult的情况下播放2个异步Web服务调用

时间:2012-08-30 16:04:13

标签: playframework playframework-2.0

Play 2允许您通过不会阻止该线程的AsyncResult执行async webservice calls

public static Result feedTitle(String feedUrl) {
    return async(
        WS.url(feedUrl).get().map(
            new Function<WS.Response, Result>() {
                public Result apply(WS.Response response) {
                    return ok("Feed title:" + response.asJson().findPath("title"));
                }
            }
        )
    );
}

这只有在您执行简单操作(例如将WS调用的结果直接传递给用户)时才有效。但是,如果您必须对结果执行其他操作,该怎么办?

the documentation,好像你可以这样做:

Promise<Response> promise = WS.url("http://some.website.com").get();
Response response = promise.get();    // I've got the result, but I've also blocked

这显然不太理想。 是否有办法在允许Play将执行传递给其他线程的同时进行异步调用?

4 个答案:

答案 0 :(得分:1)

看看https://github.com/jroper/play-promise-presentation。对于我来说,如何设计一个可能有多个承诺调用等的系统,并将各种承诺响应操作到更复杂的响应所需的内容等等,这对我来说真的很清楚。

最好的部分是 - 这个例子并不太宽泛。它阅读得非常好,理解起来非常简单。

答案 1 :(得分:0)

好的,我找到了一个解决方案,虽然有点冗长。你能做的就是把所有东西都搬到一个演员身上。

在Global对象中设置Actor,但将ActorRef保存在您可以访问的位置。

ActorRef myActor = Akka.system().actorOf(new Props(MyActor.class));

在你的演员中,进行WS调用。

public void onReceive(Object message) {
    WSRequestHolder request = WS.url("http://example.com");
    Response response = request.get().get();
    SomeResult result = doFurtherProcessing(response);
    getContext().sender().tell(result);    // reply the asking thread
}

在你的控制器中,只需将调用包装在async()

public static Result method() {
    String message = "hello";
    return async(
        Akka.asPromise(ask(myActor, message, 1000)).map(new Function<Object, Result>() {
            public Result apply(Object result) throws Throwable {
                return ok(result);
            }
        })
    );
}

参考:http://www.playframework.org/documentation/2.0.3/JavaAkka

答案 2 :(得分:0)

不需要创建actor来推迟计算。 您可以使用Promise.map(Function<A, B>)Promise.flatMap(Function<<A>, Promise<B>>)将后处理绑定到异步调用。这些电话是可以链接的。

示例:

return async(WS.url("http://someservice.com/").get().map(
        new F.Function<play.libs.WS.Response, SomeData>() {
            @Override
            public SomeData apply (play.libs.WS.Response response) throws Throwable {
                SomeData someData = computeData(response);
                // Do extra computing here
                return someData;
            }
        }).map(
        new F.Function<SomeData, Result>() {
            @Override
            public Result apply(SomeData someData) throws Throwable {
                Result result = doSomethingElse(someData);
                return ok(result); 
            }
        })
 );

只要最后一次map返回Result,您就可以了。

答案 3 :(得分:0)

play2的替代答案

String url1 = String.format( "http://localhost:8080/site1", langcode );
String url2 = String.format( "http://localhost:8080/site2", langcode );
String url3 = String.format( "http://localhost:8080/site3", langcode );

F.Promise<WS.Response> remoteCall1 = WS.url(url1).get();
F.Promise<WS.Response> remoteCall2 = WS.url(url2).get();
F.Promise<WS.Response> remoteCall3 = WS.url(url3).get();

F.Promise<java.util.List<WS.Response>> promisesSequence = F.Promise.sequence(remoteCall1, remoteCall2, remoteCall3);

F.Promise<Result> resultPromise = promisesSequence.map(
    new Function<java.util.List<WS.Response>, Result>(){
        @Override
        public Result apply(java.util.List<WS.Response> responses){
            final StringBuffer sb = new StringBuffer();
            for(WS.Response r : responses){
                sb.append(r.getBody());
            }
            return ok( main.render("Output", sb.toString()));
        }
    });