Introduction à Finagle

1. Vue d'ensemble

Dans ce tutoriel, nous allons jeter un coup d'œil à Finagle, la bibliothèque RPC de Twitter.

Nous l'utiliserons pour créer un client et un serveur simples.

2. Blocs de construction

Avant de nous plonger dans l'implémentation, nous devons apprendre à connaître les concepts de base que nous utiliserons pour créer notre application. Ils sont largement connus mais peuvent avoir une signification légèrement différente dans le monde de Finagle.

2.1. Prestations de service

Les services sont des fonctions représentées par des classes qui prennent des requêtes et renvoient un Future contenant le résultat éventuel de l'opération ou des informations sur l'échec.

2.2. Filtres

Les filtres sont également des fonctions. Ils prennent une demande et un service, font certaines opérations sur la demande, la transmettent au service, font quelques opérations sur le futur résultant , et finalement retournent le futur final . Nous pouvons les considérer comme des aspects car ils peuvent implémenter la logique qui se produit autour de l'exécution d'une fonction et modifier son entrée et sa sortie.

2.3. Futures

Les contrats à terme représentent les résultats éventuels des opérations asynchrones. Ils peuvent être dans l'un des trois états: en attente, réussi ou échoué.

3. Service

Tout d'abord, nous allons implémenter un service d'accueil HTTP simple. Il prendra le paramètre de nom de la demande et répondra et ajoutera le message «Bonjour» habituel.

Pour ce faire, nous devons créer une classe qui étendra la classe Service abstraite de la bibliothèque Finagle, en implémentant sa méthode apply .

Ce que nous faisons ressemble à l'implémentation d'une interface fonctionnelle. Fait intéressant, cependant, nous ne pouvons pas utiliser cette fonctionnalité spécifique car Finagle est écrit en Scala et nous profitons de l'interopérabilité 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. Filtre

Ensuite, nous allons écrire un filtre qui enregistrera certaines données sur la demande dans la console. Semblable à Service , nous devrons implémenter la méthode apply de Filter qui prendra la requête et retournera une réponse Future , mais cette fois, elle prendra également le service comme deuxième paramètre.

La classe Filter de base a quatre paramètres de type mais très souvent nous n'avons pas besoin de changer les types de requêtes et de réponses à l'intérieur du filtre.

Pour cela, nous utiliserons le SimpleFilter qui fusionne les quatre paramètres de type en deux. Nous imprimerons certaines informations de la requête, puis invoquerons simplement la méthode apply du service fourni:

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. Serveur

Nous pouvons maintenant utiliser le service et le filtre pour créer un serveur qui écoutera réellement les demandes et les traitera.

Nous allons fournir à ce serveur un service qui contient à la fois notre filtre et notre service enchaînés avec la méthode andThen :

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

6. Client

Enfin, nous avons besoin d'un client pour envoyer une requête à notre serveur.

Pour cela, nous allons créer un service HTTP en utilisant la méthode pratique newService de la classe Http de Finagle . Il sera directement responsable de l'envoi de la demande.

De plus, nous utiliserons le même filtre de journalisation que nous avons implémenté auparavant et le chaînerons avec le service HTTP. Ensuite, nous aurons juste besoin d'appeler la méthode apply .

Cette dernière opération est asynchrone et ses résultats éventuels sont stockés dans l' instance Future . Nous pourrions attendre que ce futur réussisse ou échoue, mais ce serait une opération bloquante et nous voudrions peut-être l'éviter. Au lieu de cela, nous pouvons implémenter un rappel à déclencher lorsque le futur réussit:

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); }) );

Notez que nous retournons BoxedUnit.UNIT. Returning Unit est la manière dont la Scala fait face aux méthodes vides , nous le faisons donc ici pour maintenir l'interopérabilité.

7. Résumé

Dans ce tutoriel, nous avons appris à créer un serveur HTTP simple et un client à l'aide de Finagle, ainsi qu'à établir la communication entre eux et à échanger des messages.

Comme toujours, le code source avec tous les exemples peut être trouvé sur GitHub.