Usando el ListeningExecutorService de Guava

Antes de conociera algo de Hystrix en una aplicacion que mantengo tuvimos varios problemas con un servicio de notificaciones. Este servicio dependia de otro servidor que, como todo, a veces fallaba.

El detalle es que el proceso de notificación se hacía durante el proceso principal de la aplicacion; cuando se realizaban las llamadas correspondientes al servicio el proceso se detiene o lanzaba excepción... pero esto era en el mejor de los casos, también ocurrio que no especificamos timeouts para el servicio y por lo tanto el proceso principal de la aplicacion podia quedarse varado hasta por un día (que era el timeout por defecto del socket que se abría por medio de otra libreria).

En fin, el perder las notificaciones no era opción y tampoco lo era detener el proceso hasta que estas notificaciones se entregaran. Por lo tanto, optamos por hacer algo como esto:

Un servicio que a veces falla

Supongamos que tenemos un servicio; que de vez en cuando da problemas:

 

Ahora, este servicio es totalmente síncrono y, para fines de este post, ¡No queremos que lo sea! Entonces, creamos una nueva interface.

Un servicio que a veces falla, pero asincrono... o eso parece

 

Y pues para poder usar un servicio no sincrono, usamos el patron de diseño adapter para realizar algo similar a esto:

 

Y entonces facilmente podriamos mandar a ejecutar el servicio y después obtener el resultado usando el   que nos regresa; aplicando el timeout usando el metodo   y si llega a pasar esto, deberíamos interrumpir el hilo que ejecuta esa llamada con   pasando como parametro true. E.g.

 

Pero si nos damos cuenta, no hay mucha diferencia entre hacer esto y ejecutar   directamente; excepto por el aplicar el timeout. Esto es porque aunque ese bloque de código se ejecuta en otro hilo, tenemos que bloquear el hilo actual (u otro) en Future.get(long, TimeUnit).

Un servicio que a veces falla, asíncrono y con callbacks que no bloquean

Para no bloquear el hilo que hace las llamadas, Guava nos ofrece un decorador para nuestros ExecutorService: ListeningExecutorService.

Al usar un ListeningExecutorService, en lugar de obtener instancias de Future, obtenemos instancias de ListenableFuture; el cual nos permite ejecutar codigo en los siguientes escenarios:

  • Cuando la computacion que se ejecuto termina satisfactoriamente; regresando un valor.
  • Cuando la computacion termina excepcionalmente i.e. lanzo una instancia de Throwable.

Ahora, si inspeccionamos la clase ListenableFuture, veremos el metodo  . Pero ustedes se preguntaran ¿Como obtenemos el valor o la excepción si el listener es un  ? Pues usando una clase de utileria de Guava:

 

Entonces ya armados con esto, podemos hacer un decorador para el servicio que nos permita agregar listeners (porque puedes agregar mas de uno) que se ejecuten en un Executor al completar la llamada al servicio

 

Y usarlo para nuestros fines
 

Lo que falto de ver

Si este tema les parecio interesante, hay muchas mas que puedes hacer con los ListenableFutures. El wiki de Guava explica todo esto, entre lo que puedes encontrar esta como transformar futuros (i.e. composicion) aplicando una funcion al valor que regresan; agregar varios futuros del mismo tipo en uno solo que regresa una lista; obtener solo los resultados de aquellos futuros que terminaron correctamente y un largo etc. etc.