Introduzione a Vert.x

1. Panoramica

In questo articolo, discuteremo di Vert.x, tratteremo i suoi concetti fondamentali e creeremo un semplice servizio web RESTfull con esso.

Inizieremo coprendo i concetti di base sul toolkit, passeremo lentamente a un server HTTP e quindi creeremo il servizio RESTfull.

2. Informazioni su Vert.x

Vert.x è un toolkit di sviluppo software open source, reattivo e poliglotta dagli sviluppatori di Eclipse.

La programmazione reattiva è un paradigma di programmazione, associato a flussi asincroni, che rispondono a qualsiasi cambiamento o evento.

Allo stesso modo, Vert.x utilizza un bus di eventi per comunicare con parti diverse dell'applicazione e trasmette gli eventi in modo asincrono ai gestori quando disponibili.

Lo chiamiamo poliglotta per il suo supporto per più linguaggi JVM e non JVM come Java, Groovy, Ruby, Python e JavaScript.

3. Configurazione

Per utilizzare Vert.x dobbiamo aggiungere la dipendenza Maven:

 io.vertx vertx-core 3.4.1 

L'ultima versione della dipendenza può essere trovata qui.

3. Vertici

I vertici sono parti di codice che il motore Vert.x esegue. Il toolkit ci fornisce molte classi verticle astratte, che possono essere estese e implementate come vogliamo.

Essendo poliglotta, i verticles possono essere scritti in una qualsiasi delle lingue supportate. Un'applicazione è tipicamente composta da più vertici in esecuzione nella stessa istanza Vert.x e comunicano tra loro utilizzando gli eventi tramite il bus degli eventi.

Per creare un verticle in JAVA, la classe deve implementare l' interfaccia io.vertx.core.Verticle o una qualsiasi delle sue sottoclassi.

4. Event Bus

È il sistema nervoso di qualsiasi applicazione Vert.x.

Essendo reattivi, i vertici rimangono dormienti finché non ricevono un messaggio o un evento. I vertici comunicano tra loro tramite il bus degli eventi. Il messaggio può essere qualsiasi cosa, da una stringa a un oggetto complesso.

La gestione dei messaggi è idealmente asincrona, i messaggi vengono accodati al bus degli eventi e il controllo viene restituito al mittente. Successivamente viene rimosso dalla coda al vertice in ascolto. La risposta viene inviata utilizzando i metodi Future e callback .

5. Semplice applicazione Vert.x

Creiamo una semplice applicazione con un verticle e distribuiamola utilizzando un'istanza vertx . Per creare il nostro verticle, estenderemo il file

Per creare il nostro verticle, estenderemo la classe io.vertx.core.AbstractVerticle e sovrascriveremo il metodo start () :

public class HelloVerticle extends AbstractVerticle { @Override public void start(Future future) { LOGGER.info("Welcome to Vertx"); } }

Il metodo start () verrà invocato dall'istanza vertx quando viene distribuito il verticle . Il metodo accetta io.vertx.core.Future come parametro, che può essere utilizzato per scoprire lo stato di una distribuzione asincrona del verticle.

Ora distribuiamo il verticle:

public static void main(String[] args) { Vertx vertx = Vertx.vertx(); vertx.deployVerticle(new HelloVerticle()); }

Allo stesso modo, possiamo sovrascrivere il metodo stop () dalla classe AbstractVerticle , che verrà invocata durante la chiusura del verticle:

@Override public void stop() { LOGGER.info("Shutting down application"); }

6. Server HTTP

Ora facciamo girare un server HTTP usando un verticle:

@Override public void start(Future future) { vertx.createHttpServer() .requestHandler(r -> r.response().end("Welcome to Vert.x Intro"); }) .listen(config().getInteger("http.port", 9090), result -> { if (result.succeeded()) { future.complete(); } else { future.fail(result.cause()); } }); }

Abbiamo sovrascritto il metodo start () per creare un server HTTP e vi abbiamo allegato un gestore di richieste. Il metodo requestHandler () viene chiamato ogni volta che il server riceve una richiesta.

Infine, il server è associato a una porta e un gestore AsyncResult viene passato al metodo listen () indipendentemente dal fatto che la connessione o l'avvio del server siano riusciti o meno utilizzando future.complete () o future.fail () nel caso di qualsiasi errori.

Notare che: il metodo config.getInteger () , sta leggendo il valore per la configurazione della porta HTTP che viene caricata da un file conf.json esterno .

Testiamo il nostro server:

@Test public void whenReceivedResponse_thenSuccess(TestContext testContext) { Async async = testContext.async(); vertx.createHttpClient() .getNow(port, "localhost", "/", response -> { response.handler(responseBody -> { testContext.assertTrue(responseBody.toString().contains("Hello")); async.complete(); }); }); }

Per il test, usiamo vertx-unit insieme a JUnit .:

 io.vertx vertx-unit 3.4.1 test 

Possiamo ottenere l'ultima versione qui.

Il verticle viene distribuito e in un'istanza vertx nel metodo setup () dello unit test:

@Before public void setup(TestContext testContext) { vertx = Vertx.vertx(); vertx.deployVerticle(SimpleServerVerticle.class.getName(), testContext.asyncAssertSuccess()); }

Allo stesso modo, l' istanza vertx viene chiusa nel metodo @AfterClass tearDown () :

@After public void tearDown(TestContext testContext) { vertx.close(testContext.asyncAssertSuccess()); }

Si noti che il metodo @BeforeClass setup () accetta un argomento TestContext . Questo aiuta a controllare e testare il comportamento asincrono del test. Ad esempio, la distribuzione verticale è asincrona, quindi in pratica non possiamo testare nulla a meno che non sia distribuita correttamente.

Abbiamo un secondo parametro al deployVerticle () il metodo, testContext.asyncAssertSuccess (). Serve per sapere se il server è stato distribuito correttamente o se si sono verificati errori. Attende la chiamata di future.complete () o future.fail () nel server verticle. In caso di fallimento, fallisce il test.

7. WebService RESTful

Abbiamo creato un server HTTP, ora usiamolo per ospitare un WebService RESTfull. Per farlo avremo bisogno di un altro modulo Vert.x chiamato vertx-web . Questo fornisce molte funzionalità aggiuntive per lo sviluppo web oltre a vertx-core .

Aggiungiamo la dipendenza al nostro pom.xml:

 io.vertx vertx-web 3.4.1 

Possiamo trovare l'ultima versione qui.

7.1. Router e percorsi

Creiamo un router per il nostro WebService. Questo router prenderà un percorso semplice del metodo GET e del metodo del gestore getArtilces () :

Router router = Router.router(vertx); router.get("/api/baeldung/articles/article/:id") .handler(this::getArticles);

Il metodo getArticle () è un metodo semplice che restituisce un nuovo oggetto Article :

private void getArticles(RoutingContext routingContext) { String articleId = routingContext.request() .getParam("id"); Article article = new Article(articleId, "This is an intro to vertx", "baeldung", "01-02-2017", 1578); routingContext.response() .putHeader("content-type", "application/json") .setStatusCode(200) .end(Json.encodePrettily(article)); }

Un router, quando riceve una richiesta, cerca il percorso corrispondente e passa ulteriormente la richiesta. Le rotte a cui è associato un metodo di gestione per eseguire il riepilogo con la richiesta.

In our case, the handler invokes the getArticle() method. It receives the routingContext object as an argument. Derives the path parameter id, and creates an Article object with it.

In the last part of the method, let's invoke the response() method on the routingContext object and put the headers, set the HTTP response code, and end the response using the JSON encoded article object.

7.2. Adding Router to Server

Now let's add the router, created in the previous section to the HTTP server:

vertx.createHttpServer() .requestHandler(router::accept) .listen(config().getInteger("http.port", 8080), result -> { if (result.succeeded()) { future.complete(); } else { future.fail(result.cause()); } });

Notice that we have added requestHandler(router::accept) to the server. This instructs the server, to invoke the accept() of the router object when any request is received.

Now let's test our WebService:

@Test public void givenId_whenReceivedArticle_thenSuccess(TestContext testContext) { Async async = testContext.async(); vertx.createHttpClient() .getNow(8080, "localhost", "/api/baeldung/articles/article/12345", response -> { response.handler(responseBody -> { testContext.assertTrue( responseBody.toString().contains("\"id\" : \"12345\"")); async.complete(); }); }); }

8. Packaging Vert.x Application

To package the application as a deployable Java Archive (.jar) let's use Maven Shade plugin and the configurations in the execution tag:

    io.vertx.core.Starter com.baeldung.SimpleServerVerticle      ${project.build.directory}/${project.artifactId}-${project.version}-app.jar  

In the manifestEntries, Main-Verticle indicates the starting point of the application and the Main-Class is a Vert.x class which, creates the vertx instance and deploys the Main-Verticle.

9. Conclusion

In questo articolo introduttivo, abbiamo discusso del toolkit Vert.x e dei suoi concetti fondamentali. Ho visto come creare un server HTTP, con Vert.x e anche un WebService RESTFull e mostrato come testarli usando vertx-unit .

Infine ha impacchettato l'applicazione come un jar eseguibile.

L'implementazione completa degli snippet di codice è disponibile su GitHub.