Introducción a Finagle

1. Información general

En este tutorial, echaremos un vistazo rápido a Finagle, la biblioteca RPC de Twitter.

Lo usaremos para construir un cliente y un servidor simples.

2. Bloques de construcción

Antes de profundizar en la implementación, necesitamos conocer los conceptos básicos que usaremos para construir nuestra aplicación. Son ampliamente conocidos, pero pueden tener un significado ligeramente diferente en el mundo de Finagle.

2.1. Servicios

Los servicios son funciones representadas por clases que toman solicitudes y devuelven un futuro que contiene el resultado final de la operación o información sobre la falla.

2.2. Filtros

Los filtros también son funciones. Toman una solicitud y un servicio, realizan algunas operaciones en la solicitud, la pasan al servicio, realizan algunas operaciones en el Future resultante y, finalmente, devuelven el Future final . Podemos pensar en ellos como aspectos, ya que pueden implementar la lógica que ocurre alrededor de la ejecución de una función y alterar su entrada y salida.

2.3. Futuros

Los futuros representan los resultados finales de las operaciones asincrónicas. Pueden estar en uno de los tres estados: pendiente, exitoso o fallido.

3. Servicio

Primero, implementaremos un servicio de saludo HTTP simple. Tomará el parámetro de nombre de la solicitud y responderá y agregará el mensaje "Hola" habitual.

Para hacerlo, necesitamos crear una clase que extienda la clase de servicio abstracta de la biblioteca Finagle, implementando su método de aplicación .

Lo que estamos haciendo es similar a implementar una interfaz funcional. Curiosamente, sin embargo, no podemos usar esa característica específica porque Finagle está escrito en Scala y estamos aprovechando la interoperabilidad Java-Scala:

public class GreetingService extends Service { @Override public Future apply(Request request) { String greeting = "Hello " + request.getParam("name"); Reader reader = Reader.fromBuf(new Buf.ByteArray(greeting.getBytes(), 0, greeting.length())); return Future.value(Response.apply(request.version(), Status.Ok(), reader)); } }

4. Filtrar

A continuación, escribiremos un filtro que registrará algunos datos sobre la solicitud en la consola. Al igual que en servicio , tendremos que aplicar filtro 's se aplican el método que va a tomar petición y devuelve una futura respuesta, pero esta vez también vamos a tomar el servicio como el segundo parámetro.

La clase de filtro básica tiene cuatro parámetros de tipo, pero muy a menudo no necesitamos cambiar los tipos de solicitudes y respuestas dentro del filtro.

Para eso, usaremos el SimpleFilter que fusiona los cuatro parámetros de tipo en dos. Vamos a imprimir algo de información de la solicitud y luego simplemente invocamos la aplican el método del servicio prestado:

public class LogFilter extends SimpleFilter { @Override public Future apply(Request request, Service service) { logger.info("Request host:" + request.host().getOrElse(() -> "")); logger.info("Request params:"); request.getParams().forEach(entry -> logger.info("\t" + entry.getKey() + " : " + entry.getValue())); return service.apply(request); } } 

5. Servidor

Ahora podemos usar el servicio y el filtro para construir un servidor que realmente escuche las solicitudes y las procese.

Aprovisionaremos este servidor con un servicio que contiene tanto nuestro filtro como el servicio encadenado junto con el método andThen :

Service serverService = new LogFilter().andThen(new GreetingService()); Http.serve(":8080", serverService);

6. Cliente

Finalmente, necesitamos un cliente para enviar una solicitud a nuestro servidor.

Para eso, crearemos un servicio HTTP usando el conveniente método newService de la clase Http de Finagle . Será el responsable directo de enviar la solicitud.

Además, usaremos el mismo filtro de registro que implementamos antes y lo encadenaremos con el servicio HTTP. Luego, solo necesitaremos invocar el método de aplicación .

Esa última operación es asincrónica y sus resultados finales se almacenan en la instancia de Future . Podríamos esperar a que este futuro tenga éxito o falle, pero esa sería una operación de bloqueo y es posible que deseemos evitarla. En cambio, podemos implementar una devolución de llamada para que se active cuando el futuro tenga éxito:

Service clientService = new LogFilter().andThen(Http.newService(":8080")); Request request = Request.apply(Method.Get(), "/?name=John"); request.host("localhost"); Future response = clientService.apply(request); Await.result(response .onSuccess(r -> { assertEquals("Hello John", r.getContentString()); return BoxedUnit.UNIT; }) .onFailure(r -> { throw new RuntimeException(r); }) );

Tenga en cuenta que devolvemos BoxedUnit.UNIT. Devolver la unidad es la forma de Scala de hacer frente a los métodos vacíos , por lo que lo hacemos aquí para mantener la interoperabilidad.

7. Resumen

En este tutorial, aprendimos cómo construir un servidor HTTP simple y un cliente usando Finagle, así como también cómo establecer comunicación entre ellos e intercambiar mensajes.

Como siempre, el código fuente con todos los ejemplos se puede encontrar en GitHub.