Ejemplo sencillo con Vert.x

De ChuWiki
Saltar a: navegación, buscar

Con Vert.x podemos desplegar nuestros módulos java (y de otros lenguajes) en una plataforma distribuida. Veamos un pequeño ejemplo en que además de escribir el consabido "Hola Mundo", usaremos también el bus de eventos de Vert.x para comunicar dos de nuestros módulos.

Puedes ver todo el código de ejemplo en VertxEventBusExample


Instalar Vert.x

Vert.x es sencillo de instalar. Basta con bajarse el zip correspondiente de la página de descargase de Vert.x y desempaquetarlo en cualquier directorio de nuestro gusto. Una vez hecho, debemos meter en el directorio bin en el path de búsqueda de ejecutables de nuestro sistema operativo

Una vez hecho esto, si abrimos una ventana de comandos y escribimos vertx version deberíamos ver la versión de vertx.

C:\> vertx version
2.1RC2 (built 2014-03-26 10:56:48)


Montar un proyecto eclipse

Directorio para los .class

Para nuestro proyecto Vert.x en java, nos basta con crear un proyecto java normal con eclipse. Unicamente y para facilitarnos la vida a la hora de ejecutar con Vert.x, bien desde eclipse, bien desde el exterior, conviene que digamos que el directorio donde se dejarán los class se llamará mods\com.chuidiang.examples~vertx~1.0.0

Project-vertx.png

El directorio mods debe llamarse así puesto que es lo que por defecto buscará Vert.x. El nombre com.chuidiang.examples~vertx~1.0.0 puede ser el que tú quieras, pero que cumpla más o menos este patrón propietario~nombre~version donde

  • propietario es una cadena de texto que habitualmente se suele poner como los nombres de paquetes java, es decir, un dominio de internet al revés, como com.chuidiang.examples.
  • nombre es el nombre que queramos dar a nuestro módulo Vert.x, puede ser cualquiera.
  • versión es también una cadena cualquiera, pero que habitualmente suele ponerse como un número de versión como 1.0.0

Fichero de configuración de nuestro módulo para Vert.x

En nuestro proyecto eclipse, debemos poner en el raíz de un directorio de fuentes (src por defecto), un fichero de nombre mod.json que puede contener varias cosas, pero cuyo contenido mínimo es

{
   "main":"com.chuidiang.examples.vertx.MainVerticle",
}

"main" es fijo y detrás, separado por dos puntos, el nombre completo de nuestra clase prncipal dentro del módulo Vert.x.

A mí no me gusta mezclar estos ficheros que no son fuentes java con las fuentes java, así que en eclipse creo en el proyecto un nuevo directorios de fuentes de nombre resources. Click derecho sobre el proyecto para sacar el menú, elegimos "Properties". Se abre una ventana y en ella elegimos "Java Build Path", pestaña "Source", botón "Add folder". En la nueva ventana que aparece pulsamos el botón "Create New Folder" y ponemos de nombre "resources". Aceptar, aceptar, aceptar. En ese nuevo directorio "resources" ponemos el fichero mod.json

Dependencias

Debemos añadir como dependencias de nuestro proyecto todas las librerías que hay en el directorio lib de donde hemos instalado Vert.x. En mi caso son las siguientes

E:\LIBRERIAS\vert.x-2.1RC2\lib> dir
26/03/2014  11:56         1.846.666 hazelcast-2.6.7.jar
26/03/2014  11:56            33.491 jackson-annotations-2.2.2.jar
26/03/2014  11:56           191.738 jackson-core-2.2.2.jar
26/03/2014  11:56           866.104 jackson-databind-2.2.2.jar
26/03/2014  11:56         1.613.159 netty-all-4.0.17.Final.jar
26/03/2014  11:56           664.749 vertx-core-2.1RC2.jar
26/03/2014  11:56            20.577 vertx-hazelcast-2.1RC2.jar
26/03/2014  11:56           181.157 vertx-platform-2.1RC2.jar

No todas son necesarias según lo que queramos usar de Vert.x, pero las añadimos todas para evitar complicaciones.

Nuestro código java

Creamos una clase cuyo nombre debería ser el que hemos puesto en el fichero mod.json, en nuestro caso com.chuidiang.examples.vertx.MainVerticle. Esta clase debe heredar de Verticle, que es una clase propia de Vert.x

La clase debe sobreescribir el método start() de la clase padre Verticle. Es a este método al que se llamará cuando se ejecute nuestro módulo en Vert.x. En ese método, ponemos nuestro consabido "Hola Mundo"

package com.chuidiang.examples.vertx;

import org.vertx.java.platform.Verticle;


public class MainVerticle extends Verticle {
   @Override
   public void start() {
      System.out.println("Hello World");
   }
}

Y listo, no hay que hacer más.

Ejecutar desde fuera de eclipse

Si hemos hecho todo bien, podemos abir una ventana de comandos de Windows (bash de linux) que tenga correctamente configurado el PATH de búsqueda de ejecutables para que encuentre el ejecutable vertx, vamos al directorio de nuestro proyecto y ejecutamos de la siguiente forma

E:\PROYECTOS\VERTX_EXAMPLES\VertxEventBusExample>vertx runmod com.chuidiang.examples~vertx~1.0.0
Hello World
Succeeded in deploying module

El comando es vertx, al que pasamos como primer parámetro runmod para indicar que queremos arrancar nuestro módulo y como segundo parámetro el nombre del módulo. Vert.x buscará el módulo dentro de un directorio de nombre mods situado donde arranquemos vertx, por eso debemos irnos al directorio del proyecto. Y dentro de mods buscará un directorio que coincida con el nombre completo del módulo com.chuidiang.examples~vertx~1.0.0. Ahí dentro deben estar los .class de nuestro módulo y sus ficheros de recursos.


Ejecutar en eclipse

Desde eclipse no podemos arrancar fácilmente el ejecutable vertx, pero afortunadamente, vertx nos ofrece una API para poder arrancarlo desde java. Simplemente debemos hacer una clase que tenga un main() como este

   public static void main(String[] args) {

      PlatformManager pm = PlatformLocator.factory.createPlatformManager();
      
      pm.deployModule("com.chuidiang.examples~vertx~1.0.0", null, 1, new Handler<AsyncResult<String>>() {
         
         @Override
         public void handle(AsyncResult<String> arg0) {
            if (arg0.succeeded()){
               System.out.println("Successful !!");
            } else {
               System.out.println("Failed !!");
            }
         }
      });
   }

Se obtiene un PlatformManager con PlatformLocator.factory.createPlatformManager(). Con eso ya tenemos nuestro servidor vertx arrancado. Ahora sólo nos queda decirle qué módulos desplegar por medio del método deployModule(). Este método admite los siguientes parámetros:

  • Nombre completo del módulo com.chuidiang.examples~vertx~1.0.0
  • Una instancia de JsonObject que sería una posible configuración que queramos pasar a nuestro módulo y que el código de nuestro módulo deberá interpretar. Por ejemplo, aquí podrían ir cosas como parámetros de conexión a una base de datos, a un servidor externo, etc. Podemos poner null si no queremos pasar ningún tipo de configuración.
  • Número de instancias que queremos de nuestro módulo. Normalmente será 1, pero podemos querer que nuestro módulo se instancie más veces.
  • Un Handler al que vertx llamará cuando termine de desplegar nuestro módulo, bien con éxito, bien con fallo. Podemos poner null si no nos interesa saber el resultado del despliegue.

Con esto, sólo tenemos que arrancar nuestra clase con el main de la forma habitual en eclipse. vertx, al igual que antes, buscará el módulo en un directorio mods en el raíz de nuestro proyecto (en el directorio de ejecución) y dentro de él en un subdirectorio com.chuidiang.examples~vertx~1.0.0, que es justo con el que hemos configurado nuestro proyecto como directorio para dejar los .class compilados.


Múltiples Verticles

Un módulo puede contener muchos Verticles, es decir, muchas clases que heredan de Verticle con su método start() y que queremos que se ejecuten al desplegar el módulo. vertx sólo llamará al método start() del Verticle que hemos configurado como principal en el fichero de configuración mod.json. El despliegue de los demás Verticles nos toca hacerlo a nosotros con código.

Todo Verticle, nuestro MainVerticle incluido, tienen definido un atributo container por heredar de la clase Verticle. Dicon container representa al contenedor vertx y tiene métodos que nos permiten arrancar y parar otros Verticles en nuestro código. Si tuvieramos dos clases más que hereden de Verticle, por ejemplo, PublisherVerticle y SubscriberVerticle, el código para desplegarlos sería el siguiente

public class MainVerticle extends Verticle {

   /** It deploys PublisherVerticle and SubscriberVerticle */
   @Override
   public void start() {
      container.deployVerticle(PublisherVerticle.class.getCanonicalName());
      container.deployVerticle(SubscriberVerticle.class.getCanonicalName(),3);
   }
}

Hay varios métodos deployVerticle con varios parámetros. Veamos algunos

  • Nombre de la clase Verticle, algo como "com.chuidiang.examples.vertx.PublisherVerticle". Una forma de obtener esta cadena es con PublisherVerticle.class.getCanonicalName()
  • Número de instancias que se quieren del Verticle, 1 por defecto. En el caso de SubscribeVerticle hemos puesto 3.
  • Podríamos poner un Handler para que vertx nos avise si ha tenido éxito o no al desplegar el Verticle.


Publicación y Subscripción en el EventBus de Vert.x

Vert.x ofrece un bus de mensajes (eventos) que permite a los módulos (Verticles) comunicarse entre sí por medio del envío y recepción de mensajes. Todo Verticle, por heredar de Verticle tiene definido un atributo vertx. Este atributo tiene un método eventBus() que nos permite obtener este bus de eventos.

Publish

El eventBus tiene dos tipos de métodos para envío de mensajes: publish y send. Si enviamos un mensaje con publish será entregado a todos los Verticles que se hayan suscrito a ese mensaje. Si enviamos un mensaje con send, sólo se entregará a uno de los Verticles que se hayan suscrito, al que decida vertx. vertx tratará de hacer que los sucesivos mensajes se repartan de forma equitativa entre los distintos Verticle receptores, siguiendo un algoritmo round-robin, aunque no garantiza este orden en todas las ocasiones. vertx tampoco garantiza la entrega de mensajes, ni con publish ni con send, por lo que cae de nuestro lado implementar algún tipo de protocolo de ACK y reenvíos si lo consideramos necesarios.

Nuestro PublisherVerticle podría ser como el siguiente

package com.chuidiang.examples.vertx;

import org.vertx.java.core.Handler;
import org.vertx.java.platform.Verticle;

/**
 * It publish a message once per second.
 * @author Chuidiang
 *
 */
public class PublisherVerticle extends Verticle {
   private int counter=0;
   
   @Override
   public void start() {
      System.out.println("Publisher started");
      
      long timerId = vertx.setPeriodic(1000, new Handler<Long>() {
         @Override
         public void handle(Long arg0) {
            vertx.eventBus().publish("message","Message number "+counter);
            System.out.println("sent " + counter++);
         }
      });
   }
}

Podemos indicarle a vertx que nos avise cada cierto tiempo, una especie de timer. Para ello, usando el atributo vertx que tenemos disponible, llamamos al método setPeriodic(). Como primer parámetro pasamos cada cuantos milisegundos queremos recibir el aviso y como segundo parámetro un Handler que será a donde nos llamará vertx cada vez que llegue el aviso. Usaremos este para envíar por el eventBus un mensaje cada segundo.

Dentro del Handler, simplemente llamamos al método publish de eventBus. publish admite dos parámetros:

  • Un String para identificar una cola de mensajes o dirección, puede ser cualquier String que queramos. Los subscritores deberán subscribirse usando este nombre, de forma que sólo recibirán los mensajes de esta cola o dirección. Esto permite enviar mensajes a subscriptores concretos.
  • El mensaje. Hay varios métodos publis con distintos tipos de dato para este segundo parámetro, podemos usar el que queramos (Boolean, Integer, JsonObject, String ...)

Listo, este Verticle, una vez desplegado, enviará un mensajes al eventBus, en la cola "message".

Subscribe

Para subscribirse, basta llamar al método registerHandler de eventBus, indicando el nombre de la cola de mensajes a la que queremos subscribirnos y un Handler para que vertx nos avise cuando llegue un mensaje. El código del Subscritpor puede ser este

package com.chuidiang.examples.vertx;

import org.vertx.java.core.Handler;
import org.vertx.java.core.eventbus.Message;
import org.vertx.java.platform.Verticle;

/**
 * It subscribes to event bus and prints messages received.
 * @author Chuidiang
 *
 */
public class SubscriberVerticle extends Verticle {
   private static int id = 0;
   private int myId = id++;

   @Override
   public void start() {
      System.out.println("consumer started " + myId);
      vertx.eventBus().registerHandler("message", new Handler<Message>() {

         @Override
         public void handle(Message arg0) {
            System.out.println("" + arg0.body() + " received by " + myId);

         }
      });
   }
}

En el método handle recibiremos una instancia de Message. El método body() nos dará el contenido del mensaje.