2012-07-17 7 views
13

En una aplicación Play Framework 2.0.1 (Scala), estamos utilizando una biblioteca cliente del servicio web que devuelve java.util.concurrent.Future como respuestas.¿Cómo envuelvo un java.util.concurrent.Future en un Akka Future?

En lugar de bloquear la aplicación Play en la llamada get(), nos gustaría envolver el j.u.c.Future en un akka.dispatch.Future, por lo que podemos utilizar simplemente el procesamiento del marco juego AsyncResult.

¿Alguien ha hecho esto antes, o tiene una biblioteca o código de ejemplo?


ACTUALIZACIÓN: Lo más parecido que hemos encontrado es este grupo de debate de Google: https://groups.google.com/forum/#!topic/play-framework/c4DOOtGF50c

... si todo lo que tiene es una llanura jucFuture lo mejor que puede hacer para crear una solución que no bloquea es tomar jucFuture y Promise, y darles un hilo que ejecute un ciclo de sondeo que completará la promesa con el resultado del futuro cuando esté listo.

¿Alguien tiene un ejemplo de implementación de esto?

Respuesta

7

@Viktor Klang: Entendemos que j.u.c.Future es una abominación. Pero eso es lo que estamos obteniendo de una pieza de software que debemos aceptar como dada por el momento.

Hasta ahora, esto es lo que hemos hackeado:

def wrapJavaFutureInAkkaFuture[T](javaFuture: java.util.concurrent.Future[T], maybeTimeout: Option[Duration] = None)(implicit system: ActorSystem): akka.dispatch.Future[T] = { 
    val promise = new akka.dispatch.DefaultPromise[T] 
    pollJavaFutureUntilDoneOrCancelled(javaFuture, promise, maybeTimeout.map(_.fromNow)) 
    promise 
} 

En otras palabras, crear un Akka separada Promise (el lado de escritura de un Future) correspondiente a la j.u.c.Future, da inicio a la devolución de llamada pollJavaFutureUntilDoneOrCancelled para actualizar la Promesa al sondear la "abominación", y devuelve la Promesa a la persona que llama.

Entonces, ¿cómo "encuestamos" para actualizar la promesa de Akka en función del estado de la j.u.c.Future?

def pollJavaFutureUntilDoneOrCancelled[T](javaFuture: java.util.concurrent.Future[T], promise: akka.dispatch.Promise[T], maybeDeadline: Option[Deadline] = None)(implicit system: ActorSystem) { 
    if (maybeDeadline.exists(_.isOverdue)) javaFuture.cancel(true); 

    if (javaFuture.isDone || javaFuture.isCancelled) { 
    promise.complete(allCatch either { javaFuture.get }) 
    } else { 
    Play.maybeApplication.foreach { implicit app => 
     system.scheduler.scheduleOnce(50 milliseconds) { 
     pollJavaFutureUntilDoneOrCancelled(javaFuture, promise, maybeDeadline) 
     } 
    } 
    } 
} 

Esto es un intento de lo que se insinuó en la discusión de grupos de google a la que hice referencia en la pregunta. Utiliza el programador Akka para devolverse cada 50 ms y comprobar si el j.u.c.Future se ha completado o cancelado. Cuando eso sucede, actualiza la promesa de Akka con el estado completado.

@Victor Klang, et al:

¿Es esta la mejor práctica? ¿Conoces una mejor manera de hacer esto? ¿Estamos perdiendo una desventaja aquí que deberíamos saber?

Gracias por más ayuda.

+0

Una desventaja obvia es, que en el peor de los casos esto causará un alto retardo de la respuesta. Si tiene, por ejemplo, la configuración predeterminada y su futuro completa 1 ms después de la verificación, puede causar un retraso de aproximadamente 100 ms. Sin embargo, esto se puede ajustar configurando el 'programador.tick-duration' en la configuración. – drexin

+0

@drexin cierto, pero una duración de tilde y una compensación de frecuencia de sondeo estarán presentes en cualquier solución basada en sondeo, ¿verdad? –

+1

Claro, pero como usted pidió inconvenientes, solo quería decirle que no solo depende del parámetro de retardo de la llamada 'scheduleOnce', sino también del ajuste en la configuración de akka. Si puede vivir con un retraso, esta debería ser una solución utilizable. – drexin

0

Debe utilizar akka.dispatch.Futures.future() con java.util.concurrent.Callable:

val akkaFuture: akka.dispatch.Future[String] = akka.dispatch.Futures.future(
    new java.util.concurrent.Callable[String] { 
    def call: String = { 
     return "scala->" + javaFuture.get 
    } 
}, executionContext) 

Gist for complete example

+0

Esto esencialmente resulta en más hilos de los necesarios, uno de los cuales, y no es mejor que simplemente llamar a javaFuture.get en el hilo principal. La introducción de akka future no es beneficiosa aquí, excepto en situaciones extremas donde la compatibilidad de los componentes es absolutamente necesaria. – vishr

Cuestiones relacionadas