Introducción y ejemplo básico sobre Vert.x

Escrito por el , actualizado el .
java planeta-codigo programacion
Enlace permanente Comentarios

Las aplicaciones basadas en eventos y con E/S no bloqueante son más eficientes, requieren menos recursos y son capaces de servir más peticiones o usuarios por unidad de tiempo. En JavaScript su mejor representante es Node.js, en la plataforma JVM uno equivalente es Vert.x. Pero también tiene sus contrapartidas. En el artículo incluiré un pequeño ejemplo de Vert.x que muestre las diferencias con las aplicaciones tradicionalmente basadas en hilos y llamadas a métodos bloqueantes.

Vert.x

Java

Tradicionalmente las aplicaciones web y los servidores han manejado con un hilo cada petición. Hasta ahora ha funcionado y en muchos casos sigue siendo suficiente para el número de usuarios y peticiones a los que atienden esas aplicaciones. Pero cuando se necesita escalar a un gran volumen de peticiones y usuarios usar hilos es ineficiente y requiere escalar horizontalmente (con más servidores) que tiene costes adicionales y una mayor complejidad en la infraestructura de la aplicación. Dedicar en exclusiva un hilo por petición hasta que esta es atendida es ineficiente ya que las operaciones de entrada/salida como disco o red como un servidor de base de datos el hilo se bloquea esperando a obtener respuesta del disco o base de datos, recursos que quedan bloqueados. Por otra parte, un cambio de contexto del procesador para pasar de ejecutar un proceso o hilo a otro es una operación costosa comparada con las velocidades de los procesadores y al mismo tiempo agravada por la necesidad de contención entre los hilos en el acceso concurrente a datos.

Para aprovechar en mayor medida la capacidad de cálculo y cada vez mayor de los modernos procesadores están surgiendo servidores y frameworks con principios diferentes, basados en eventos y la programación reactiva. Unos de los más conocidos es el runtime Node.js y el servidor web Nginx. La idea de cualquiera de ellas es usar eventos que son procesados por un único hilo (o uno por núcleo del procesador) con ninguna o menor necesidad de contención entre los hilos y usando programación reactiva de modo que las operaciones de E/S no sean bloqueantes. Por otra parte menos hilos necesitarán menos recursos del sistema y también menos cambios de contexto en el procesador. Todo ello hace que usando el mismo sistema las aplicaciones basadas en eventos, programación reactiva y no bloqueantes en la E/S consuman menos memoria y sean capaces de servir más peticiones por una diferencia muy significativa que las aplicaciones basadas en hilos.

Los hilos y las operaciones bloqueantes a pesar de su ineficiencia hacen fácil la programación usando programación imperativa o al menos más que la programación reactiva en las que se usan una función de rellamada o callback por cada operación bloqueante que realicemos. También señalar que un bloqueo en el hilo que procesa las peticiones por un fallo de programación, por una operación que es bloqueante o que en su proceso acapara un tiempo significativo si no es tenida en cuenta impedirá que la aplicación procese eventos y peticiones lo que se traducirá en un aparente denegación de servicio. Esta es la contrapartida de este nuevo paradigma para las aplicaciones.

Teniendo en cuenta estos aspectos una de las opciones de la plataforma JVM es Vert.x que es el equivalente a Node.js en la plataforma JavaScript. Otra de las características de Vert.x es que es políglota de modo que podremos elegir el lenguaje de programación que prefiramos ya sea Java, JavaScript, Groovy, Ruby o Ceylon. Hay que tener en cuenta que en su mayor parte las librerías Java no han sido diseñadas para la programación reactiva y no bloqueante, para lo cual Vert.x proporciona varios módulos diseñados con esta idea, en la página de documentación están indicados cuales son, su documentación y ejemplos de uso. También Vert.x prescinde de los servidores de aplicaciones y estas se ejecutan siguiendo la nueva tendencia de un proceso por aplicación más alineado con los microservicios.

En el código del siguiente ejemplo emplearé Java por mis preferencias y motivos para seguir usándolo pero sería similar empleando cualquiera de los otros si prefirieses uno de ellos. El ejemplo Main.java es una aplicación que devuelve un mensaje en la URL http://localhost:8080, el ejemplo Server.java devuelve una cabecera y usa un Verticle y el tercero Web.java acepta un parámetro y usa una plantilla de Thymeleaf, finalmente el archivo build.gradle contiene las dependencias necesarias para los tres ejemplos y la construcción del proyecto con Gradle.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'eclipse'

sourceCompatibility = '1.8'

//mainClassName = 'io.github.picodotdev.blogbitix.vertx.helloworld.Main'
//mainClassName = 'io.github.picodotdev.blogbitix.vertx.helloworld.Server'
mainClassName = 'io.github.picodotdev.blogbitix.vertx.helloworld.Web'

def versions = [
    gradle: '2.10',
    vertx: '3.2.0',
    slf4j: '1.7.13',
    junit: '4.12'
]

repositories {
    jcenter()
}

dependencies {
    compile "io.vertx:vertx-core:$versions.vertx"
    compile "io.vertx:vertx-web:$versions.vertx"
    compile "io.vertx:vertx-web-templ-thymeleaf:$versions.vertx"
    compile "org.slf4j:slf4j-api:$versions.slf4j"

    testCompile "junit:junit:$versions.junit"
}

task wrapper(type: Wrapper) {
	gradleVersion = versions.gradle
}
build.gradle
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package io.github.picodotdev.blogbitix.vertx.helloworld;

import io.vertx.core.Vertx;

public class Main {

    public static void main(String[] args) {
        Vertx.vertx().createHttpServer().requestHandler(req -> req.response().end("Hello World!")).listen(8080, handler -> {
            if (handler.succeeded()) {
                System.out.println("Application starter, listening on http://localhost:8080/");
            } else {
                System.err.println("Failed to start application");
            }
        });
    }
}
Main.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package io.github.picodotdev.blogbitix.vertx.helloworld;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Verticle;
import io.vertx.core.Vertx;
import io.vertx.ext.web.Router;

public class Server extends AbstractVerticle {

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

    @Override
    public void start() throws Exception {
        Router router = Router.router(vertx);

        router.route().handler(routingContext -> {
            routingContext.response().putHeader("content-type", "text/html").end("Hello World!");
        });

        vertx.createHttpServer().requestHandler(router::accept).listen(8080);
    }
}
Server.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package io.github.picodotdev.blogbitix.vertx.helloworld;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Verticle;
import io.vertx.core.Vertx;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.templ.TemplateEngine;
import io.vertx.ext.web.templ.ThymeleafTemplateEngine;

public class Web extends AbstractVerticle {

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

	@Override
	public void start() throws Exception {
		Router router = Router.router(vertx);
		TemplateEngine engine = ThymeleafTemplateEngine.create();

		router.route().handler(ctx -> {
			String name = ctx.request().getParam("name");
			ctx.put("name", name);
			ctx.put("welcome", (name == null) ? "¡Hola mundo!" : String.format("¡Hola %s!", name));

			ctx.next();
		});

		router.route().handler(ctx -> {
			engine.render(ctx, "templates/index.html", res -> {
				if (res.succeeded()) {
					ctx.response().putHeader("Content-Type", "text/html; charset=utf-8");
					ctx.response().end(res.result());
				} else {
					ctx.fail(res.cause());
				}
			});
		});

		vertx.createHttpServer().requestHandler(router::accept).listen(8080);
	}
}
Web.java

La documentación de cada módulo de Vert.x está bastante bien y explicada en los diferentes lenguajes que soporta. Si la documentación no nos es suficiente en el repositorio de ejemplos veremos el código fuente completo de diversas funcionalidades.

Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew run


Comparte el artículo: