Introduzione a Finagle

1. Panoramica

In questo tutorial, daremo una rapida occhiata a Finagle, la libreria RPC di Twitter.

Lo useremo per costruire un semplice client e server.

2. Building Blocks

Prima di approfondire l'implementazione, dobbiamo conoscere i concetti di base che utilizzeremo per creare la nostra applicazione. Sono ampiamente conosciuti ma possono avere un significato leggermente diverso nel mondo di Finagle.

2.1. Servizi

I servizi sono funzioni rappresentate da classi che accettano richieste e restituiscono un Future contenente l'eventuale risultato dell'operazione o informazioni sull'errore.

2.2. Filtri

Anche i filtri sono funzioni. Prendono una richiesta e un servizio, eseguono alcune operazioni sulla richiesta, la passano al servizio, eseguono alcune operazioni sul futuro risultante e infine restituiscono il futuro finale . Possiamo pensarli come aspetti in quanto possono implementare la logica che accade attorno all'esecuzione di una funzione e alterarne l'input e l'output.

2.3. Futures

I futures rappresentano gli eventuali risultati delle operazioni asincrone. Possono trovarsi in uno dei tre stati: in sospeso, riuscito o non riuscito.

3. Servizio

Innanzitutto, implementeremo un semplice servizio di saluto HTTP. Prenderà il parametro name dalla richiesta e risponderà e aggiungerà il consueto messaggio "Hello".

Per fare ciò, dobbiamo creare una classe che estenderà la classe astratta Service dalla libreria Finagle, implementando il suo metodo apply .

Quello che stiamo facendo è simile all'implementazione di un'interfaccia funzionale. È interessante notare, tuttavia, che non possiamo effettivamente utilizzare quella caratteristica specifica perché Finagle è scritto in Scala e stiamo sfruttando l'interoperabilità 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. Filtro

Successivamente, scriveremo un filtro che registrerà alcuni dati sulla richiesta nella console. Simile al servizio , avremo bisogno di implementare filtro s' applicano il metodo che ti prende richiesta e restituire un futuro di risposta, ma questa volta sarà anche prendere il servizio come secondo parametro.

La classe Filter di base ha quattro parametri di tipo ma molto spesso non è necessario modificare i tipi di richieste e risposte all'interno del filtro.

Per questo, useremo SimpleFilter che unisce i quattro parametri di tipo in due. Stamperemo alcune informazioni dalla richiesta e quindi richiameremo semplicemente il metodo di applicazione dal servizio fornito:

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

Ora possiamo utilizzare il servizio e il filtro per creare un server che ascolterà effettivamente le richieste e le elaborerà.

Forniremo a questo server un servizio che contiene sia il nostro filtro che il servizio concatenato insieme al metodo andThen :

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

6. Cliente

Infine, abbiamo bisogno di un client per inviare una richiesta al nostro server.

Per questo, creeremo un servizio HTTP utilizzando il comodo NuovoServizio metodo dal di Finagle Http classe. Sarà direttamente responsabile dell'invio della richiesta.

Inoltre, useremo lo stesso filtro di registrazione che abbiamo implementato prima e lo concateneremo con il servizio HTTP. Quindi, dovremo solo richiamare il metodo apply .

L'ultima operazione è asincrona ei suoi risultati finali vengono archiviati nell'istanza Future . Potremmo aspettare che questo futuro abbia successo o fallisca, ma sarebbe un'operazione di blocco e potremmo volerla evitare. Invece, possiamo implementare un callback da attivare quando il futuro ha successo:

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

Notare che restituiamo BoxedUnit.UNIT. Returning Unit è il modo in cui Scala di affrontare i metodi void , quindi lo facciamo qui per mantenere l'interoperabilità.

7. Riepilogo

In questo tutorial, abbiamo imparato come costruire un semplice server HTTP e un client usando Finagle, nonché come stabilire la comunicazione tra di loro e scambiare messaggi.

Come sempre, il codice sorgente con tutti gli esempi può essere trovato su GitHub.