Harvesting verticles, parte 2.

> ¿Qué es un verticle?

Antes de comenzar debes tener instalado Vert.x. Si tienes dificultades para instalarlo crea un nuevo hilo en el foro y verifica que la pregunta no exista aun.

Vert.x usa eventos para producir acciones y las consecuencias de esas acciones pueden generar otros eventos, así sucesivamente. La forma en que los eventos trasmiten información es mediante mensajes.

Los verticles son una unidad muy pequeña de código; scripts en su mayoría, y se encargan de contener la lógica de los eventos, están pensados para ser muy pequeños y la máxima es que no estorben a los demás verticles. Sí un verticle se muere no dejara colgado a los demás. Pero si un Verticle contiene más verticles, todos los que dependan del original mueren y eso esta bien porque estamos seguros de no dejar verticles huérfanos.

Déjenme poner uno ejemplos para entender este concepto.

Imaginemos una empresa de paquetería llamada EP; tipo DHL y una familia, donde los padres están en en D.F (padres) y uno de sus hijos esta estudiando en el estado de Monterrey (universitario) y su hija esta casada y tiene hijos gemelos en el estado de México (gemelos).

Entonces tenemos:
* EP -> Empresa de paquetería.
* Padres -> D.F.
* Universitario -> Monterrey.
* Gemelos -> Edo. Méx.

Ejemplo 1: Un evento se produce cuando los padres recuerdan que ya va a ser el cumpleaños de los gemelos y le piden a EP que hagan llegar unos regalos a los nietos en el estado de México, donde el regalo es el mensaje y el evento que disparo la acción fue el cumpleaños de los gemelos.

Ejemplo 2: Otro evento se produce cuando los padres le mandan dinero y cobijas nuevas al universitario, y no los pidió, pero aun así se lo mandaron. Sí, el dinero y las cobijas son el mensaje.

Ejemplo 3: También cuando el universitario este a punto de graduarse le pide a la empresa EP que mande los boletos de la fiesta de graduación a sus padres y a toda la familia de su hermana. Otra ves los boletos son el mensaje y la acción se genero porque ya se va a graduar y quiere que su familia este presente en la fiesta.

Otros escenarios se pueden producir, como el que la familia del estado de México decida ya no ser cliente de EP, o que el universitario cambie de dirección. Todos los casos son validos y posibles.

Entonces en Vert.x ¿cómo se representa todos estos escenarios?

> ¿Cómo convierto las familias en verticles?

Antes de iniciar con la parte practica indicare como esta conformado mi ambiente:
* Editor de texto
* Terminal Bash
* $ vertx version
2.0.2-final (built 2013-10-08 10:55:59)
* $ java -version
java version "1.7.0_11"
Java(TM) SE Runtime Environment (build 1.7.0_11-b21)
Java HotSpot(TM) 64-Bit Server VM (build 23.6-b04, mixed mode)
* Groovy como lenguaje base, pero es muy facil pasar los ejemplos al lenguaje de tu preferencia.
io.vertx~lang-groovy~2.0.0-final

Todos los comando en Bash iniciaran con el símbolo $

Vert.x cuenta con un artefacto similar a EP llamado EventBus, el EventBus es una de las partes más importante y fáciles de usar, importante porque es la encargada de la comunicación mediante mensajes.

Iniciemos creado a los padres, que de ahora en adelante a las familias las llamaremos verticles.

Con nuestro editor creemos una archivo llamado “Padres.groovy” en una carpeta destinada exclusivamente a los ejercicios, con el siguiente código:

package familias

println "Inicio el verticle padres"

luego desde la terminal sobre la carpeta donde creamos nuestro verticle ejecutemos el siguiente comando:
$ vertx run Padres.groovy

Sí es la primera vez que corres(run) un verticle veras que se crea una carpeta llamada “mods” y se descarga el modulo del lenguaje Groovy, algo muy similar a lo siguiente:

Downloading io.vertx~lang-groovy~2.0.0-final. Please wait...
Downloading 100%
Module io.vertx~lang-groovy~2.0.0-final successfully installed
Inicio el verticle padres

Tomara un tiempo dependiendo de tu conexión a Internet, pero debes notar que al final de todo se escribió “Inicio el verticle padres” y esto indica que ya esta arriba nuestro primer verticle.

Podemos monitorear nuestro verticle Padre con una herramienta que esta incluida en el JDK llamada JConsole ($JAVA_HOME/bin/jconsole) y podemos matarlo con un ctrl+c, mas adelante mostrare como administrar mejor los verticles.

Matemos pues el verticle Padres, y creemos el verticle Universitario.groovy
$ vertx run Universitario.groovy

/** Universitario.groovy */
package familias

container.logger.info "Inicio el verticle universitario"

Como resultado en la consola nos muestra el texto esperado, pero nos encontramos con algo nuevo container. Si viéramos a los verticles como viajeros, las mochilas serías justamente el objeto container, le podemos meter otros verticles, sacarlos, meter variables de entorno, podemos tomar un logger único para el viajero en cuestión y pasarle datos importantes al iniciar el viaje como un mapa o mejor dicho una configuración. Por el momento solo usamos el logger del container.

Matemos al verticle del universitario. Como recordamos es con ctrl-c.

Ya para finalizar esta parte crearemos en verticle para los gemelos, Gemelos.groovy. Después de probarlo matemoslo.
$ vertx run Gemelos.groovy

/** Gemelos.groovy */
package familias

def log = container.logger

log.info "Inicio el verticle para los gemelos"

> ¿Cómo ejecuto todos los verticles al mismo tiempo?

Bueno hay varias formas de ejecutar todos nuestros verticles, pero empecemos por la mas apropiada para este escenario.

Necesitamos un nuevo verticle llamado Main.groovy

/** Main.groovy */
def log = container.logger
log.info 'Iniciando el verticle principal...'
$ vertx run Main.groovy
Iniciando el verticle principal...

Como comentamos unas lineas atrás, si vemos a los verticles como viajeros con una mochila (container), en ella podemos guardar (deploy) otros verticles y también sacarlos (undeploy), así que guardemos en el verticle Main.groovy los verticles Padre.groovy, Universitario.groovy y Gemelos.groovy.

/** Main.groovy */
package utilidades
def log = container.logger
log.info 'Iniciando el verticle principal...'

container.deployVerticle('Padres.groovy')
container.deployVerticle('Universitario.groovy')
container.deployVerticle('Gemelos.groovy')

$ vertx run Main.groovy
Iniciando el verticle principal...
Inicio el verticle universitario
Inicio el verticle para los gemelos
Inicio el verticle padres

Pero podríamos agregar al container tantos verticles de tipo Universitarios.groovy como necesitáramos. La manera en que los diferenciaríamos sería sabiendo su id con el que se registro (deploymetId), Modifiquemos nuestro verticle principal y volvamos a ejecutarlo.

/** Main.groovy */
package utilidades
def log = container.logger
log.info 'Iniciando el verticle principal...'

def verticles = [
        'Padres.groovy',
        'Universitario.groovy',
        'Universitario.groovy',
        'Universitario.groovy',
        'Gemelos.groovy',
        'Primos.groovy'
]

verticles.each { verticle ->
        container.deployVerticle(verticle) { asyncResult ->
                if(asyncResult.succeeded()) {
                        log.info "[DEPLOY:OK] ${verticle} :: ${asyncResult.result()}"
                } else {
                        log.error "[DEPLOY:ERROR] ${verticle} :: ?"
                }
        }
}

$ vertx run Main.groovy
Iniciando el verticle principal...
Inicio el verticle universitario
Inicio el verticle universitario
Inicio el verticle para los gemelos
Inicio el verticle padres
Inicio el verticle universitario
[DEPLOY:OK] Universitario.groovy :: deployment-7571738b-2738-443b-9f5d-2558f0589182
[DEPLOY:OK] Universitario.groovy :: deployment-c4dd23c9-8287-4a4d-96c4-b03f1a87fe7d
[DEPLOY:OK] Gemelos.groovy :: deployment-d988ebca-0a58-4b61-8fd9-dc4b5810bade
[DEPLOY:OK] Padres.groovy :: deployment-79c1c2c7-7b6f-4b10-818a-a29bafb7daac
[DEPLOY:OK] Universitario.groovy :: deployment-4838ddcf-0146-4203-9eec-38ab4f89aaf2
[DEPLOY:ERROR] Primos.groovy :: ?

Mas adelante veremos como se comunican los verticles, por el momento enfoquemos nuestra atención en como administrarlos, y para ello crearemos una pequeña herramienta.

> El VerticleManager.

Necesitamos desplegar y dedesplegar (no se como traducir undeploy pero usare la palabra retirar) de una forma fácil los verticles, haremos uso de un servidor TCP para conectarnos vía telnet y aplicar los cambios; mientras exploramos otras bondades de Vert.x. Podemos parar el verticle Main.groovy ya no lo necesitaremos por el momento.

Iniciemos creado un verticle llamado VerticleManager.groovy. Vert.x ofrece una forma muy fácil de crear un servidor TCP y varios otros de una forma muy consistente. Agreguemos un servidor al verticle.

/** VerticleManager.groovy */
def server = vertx.createNetServer()

Pero nuestro servido necesita una configuración, por ejemplo a que puerto escuchar, el host. Usemos el puerto 4433, por defecto ocupara 0.0.0.0 como hostname o ip adress.

/** VerticleManager.groovy */
def server = vertx.createNetServer()
server.connectHandler { sock ->
    sock.dataHandler { buffer ->
        print ">> ${buffer}"
    }
}.listen(4433)

Ejecutemos el veticle VerticleManager.groovy en una ventana de nuestra terminal y telnet en otra, luego escribamos y veamos como en la ventana de nuestro verticle se muestras las entradas (para escapar de telnet es ctrl + ], quit)

$ vertx run VerticleManager.groovy
$ telnet localhost 4433

Modifiquemos de nuevo el VerticleManager.groovy para que haga un eco de las entradas.

/** VerticleManager.groovy */
def log = container.logger
log.info "Iniciando el VerticleManager..."

def help = """\
######################################################################
# VerticleManager                                                    #
# deploy   ? [? ..]: despliega una lista de verticles por su nombre. #
# undeploy ? [? ..]: retira una lista de veticles por su deploymetId.#
######################################################################

"""

def server = vertx.createNetServer()
server.connectHandler { sock ->
        sock.write help
    sock.dataHandler { buffer ->
        print ">> ${buffer}"
        sock.write "<< ${buffer}"
    }
}.listen(4433)

Reiniciemos el VerticleManager e ingresemos por telnet, veamos como nos hace un eco de nuestras entradas, ¿fácil no?, Como notaras agregué unas especie de “spec”, son los comandos que debe soportar por el momento nuestra herramienta. Agreguemos ahora al servidor el código que maneja los comandos, solo los comandos; voy a ir muy paso a paso, no pensando en aburrirlos ni menospreciar su inteligencia.

/** VerticleManager.groovy */
def log = container.logger
log.info "Iniciando el VerticleManager..."

def help = """\
######################################################################
# VerticleManager                                                    #
# deploy   ? [? ..]: despliega una lista de verticles por su nombre. #
# undeploy ? [? ..]: retira una lista de veticles por su deploymetId.#
######################################################################

"""

def executeCommand = { input ->
    def inputList = input.toString().split()
    if (!inputList) return null

    def head = inputList.head()
    def tail = inputList.tail()

    switch (head) {
        case 'deploy':
            log.info "deploy ${tail}"
            break
        case 'undeploy':
            log.info "undeploy ${tail}"
            break
    }
}

def server = vertx.createNetServer()
server.connectHandler { sock ->
    sock.write help
    sock.dataHandler { buffer ->
        executeCommand(buffer)
    }
}.listen(4433)

Debemos dar por hecho que se tiene que reiniciar el verticle manager con cada cambio. Probemos nuestros comandos p.e.deploy hola como te va?, undeploy me va muy bien y veamos que ya no regresa un eco, pero si separa el comando y sus entradas,
Ahora vamos a ver muy superficialmente como se comunican los verticles usando el EventBus. Lo que aremos es usar el ventBus para mandar cualquier texto por el soket del servidor TCP, mandaremos la especificación, y los ecos. Más adelante explicare a detalle como usar el EventBus, separando cada opción de la especificación en su propio verticle, incloso en archivos separados.

Recordemos que el EventBus es una especie de empresa que se dedica a mandar mensajes y/o paquetes, pero para mandarlo se necesita tener una deirección a donde enviar los mesajes, nosotros para mandar mensajes al socket usaremos la dirección 'send-echo' y la asociaremos a una variable llamada sendEchoAddress, para darla de alta en el EventBus usaremos la función registerEchoSender y para mandar el eco al socket aremos uso de la función sendEcho.

/** VerticleManager.groovy */
def eb = vertx.eventBus
def log = container.logger
log.info "Iniciando el VerticleManager..."

def help = """\
######################################################################
# VerticleManager                                                    #
# deploy   ? [? ..]: despliega una lista de verticles por su nombre. #
# undeploy ? [? ..]: retira una lista de veticles por su deploymetId.#
######################################################################

"""

def sendEchoAddress = 'send-echo'

def sendEcho = { msg ->
    eb.send(sendEchoAddress, msg.toString())
}

def executeCommand = { input ->
    def inputList = input.toString().split()
    if (!inputList) return null

    def head = inputList.head()
    def tail = inputList.tail()

    switch (head) {
        case 'deploy':
            log.info "deploy ${tail}"
            sendEcho("> deploy ${tail}\n")
            break
        case 'undeploy':
            log.info "undeploy ${tail}"
            sendEcho("> undeploy ${tail}\n")
            break
    }
}

def registerEchoSender = { sock ->
    eb.registerHandler(sendEchoAddress) { msg ->
        sock.write(msg.body())
    }
}

def server = vertx.createNetServer()
server.connectHandler { sock ->
    registerEchoSender(sock)
    sendEcho(help)
    sock.dataHandler { buffer ->
        executeCommand(buffer)
    }
}.listen(4433)

Aun que aun no explicamos el uso del EventBus para registrar y retirar handlers sería muy bueno que jugaran con el código, p.e. agregando otro comando llamado 'help' y cuando llamen a ese comando muestre la especificación o ayuda. También puedes sustituir la variable de la dirección del eco y ponerla en duro, para que visualmente tengas un mejor panorama.

> Desplegando y retirando verticles mediante el VerticleManager

Ya tenemos el esqueleto necesario para administrar nuestros verticles es hora de hacer lo mismo que con el verticle Main.
Sera muy sencillo, para los comandos deploy y undeploy les pasaremos una lista de nombre y si el nombre coincide con algún verticle lo desplegaremos en caso contrario pintaremos algún error que indique que no se encontró el verticle. Pero para hacerlo mas fácil podremos usar un comodín y pasar los nombres en minúsculas, el código lo dice más claro.

/** VerticleManager.groovy */
def eb = vertx.eventBus
def log = container.logger
log.info "Iniciando el VerticleManager..."

def help = """\
########################################################################
# VerticleManager                                                      #
# deploy     ? [? ..]: despliega una lista de verticles por su nombre. #
# undeploy   ? [? ..]: retira una lista de veticles por su deploymetId.#
# show       deployed: muestra los vertilces desplegados.              #
# show     undeployed: muestra los verticles retirados                 #
########################################################################
"
""

def sendEchoAddress = 'send-echo'

def sendEcho = { msg ->
    eb.send(sendEchoAddress, msg.toString())
}

def verticlesNames = [
    'Padres.groovy',
    'Universitario.groovy',
    'Gemelos.groovy'
]

def deploymentList = [:]
def undeploymetList = []

def executeCommand = { input ->
    def inputList = input.toString().split()
    if (!inputList) return null

    def head = inputList.head()
    def tail = inputList.tail()*.toLowerCase()

    switch (head) {
        case 'deploy':
            def verticlesFound = []
            tail.each { vi -> //verticle input
                verticlesFound << verticlesNames.findAll { vf -> //verticle found
                    vf.toLowerCase().startsWith(vi)
                }                
            }
            verticlesFound.flatten().each { vn -> //verticle name
                container.deployVerticle(vn) { asyncResult ->
                    if(asyncResult.succeeded()) {
                        deploymentList << ["${asyncResult.result()}": vn]
                        sendEcho "[DEPLOY:OK] ${vn} :: ${asyncResult.result()}"
                    } else {
                        sendEcho "[DEPLOY:ERROR] ${vn} :: ${asyncResult.cause()}"
                    }
                }
            }
            break
        case 'undeploy':
                def deploymentIdFound = []
                tail.each { dii -> //deploymentId input
                    container.undeployVerticle(dii) { asyncResult ->
                        if(asyncResult.succeeded()) {
                            def vdi = deploymentList.get("${dii}") //verticle and deploymentId
                            undeploymetList << ["$dii": vdi]
                            deploymentList.remove(dii)
                            sendEcho "[UNDEPLOY:OK] ${dii} :: ${vdi}"
                        } else {
                            sendEcho asyncResult.cause()
                        }
                    }    
                }
            break
        case 'show':
                switch(tail[0]) {
                    case 'deployed':
                        deploymentList.each { id ->
                            sendEcho " * $id"
                        }
                    break
                    case 'undeployed':
                        undeploymetList.each { id ->
                            sendEcho " * $id"
                        }
                    break
                }
            break
    }
}

def registerEchoSender = { sock ->
    eb.registerHandler(sendEchoAddress) { msg ->
        sock.write(msg.body() + '\n')
    }
}

def server = vertx.createNetServer()
server.connectHandler { sock ->
    registerEchoSender(sock)
    sendEcho(help)
    sock.dataHandler { buffer ->
        executeCommand(buffer)
    }
}.listen(4433)

Como ves se agregaron otros comandos para listar los verticles desplegados y retirados. Sería interesante que jugaras con el código haciedo que la lista de nombres de verticles fuera dinámica también puedes modificar el contenido de los verticles mientras los despliegas.

En esta ocasión solo trate de dar una idea de que es un verticle administrandolo, en la siguiente entrega veremos como comunicar los verticles de una forma más extensa, les adelanto que el VerticleManager sera partido en muchos verticles.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de rodrigo salado anaya

No es la versión final

Estoy seguro que con en tiempo modificare las entradas de este tema pondré comentarios para indicar los cambio y subiré a un repositorio git los documentos como el código. Y pues esto es solo un intento de difundir esta joven plataforma. Sean duros con las criticas y sueltos con las sugerencias, se los agradeceré mucho, si no entienden algo digan que parte en especifico y si es necesario actualizare el documento o responderé en otro comentario.

Soy muy novato en esto así que es buena oportunidad para aprender de las preguntas de otros, saludos y pasenla bien.

Imagen de rodrigo salado anaya

Repo de la parte 3

Es este repo (https://github.com/rodrigoSaladoAnaya/harvestingVerticles) pondré el código de la parte 3 donde platicare sobre como se comunican los verticles. En general nos quitaremos los switch de VerticleManager.groovy y haremos todo mediante mensajes. Si siguen el historial de los commit (https://github.com/rodrigoSaladoAnaya/harvestingVerticles/commits/master) se pueden ir dando una idea de lo que escribiré en la próxima entrada.

Saludos a todos.

Esta padre ver el historial

Esta padre ver el historial de commits! :) Voy leyendo poco a poco esto de vert.x

Imagen de rodrigo salado anaya

Gracias

Gracias por tu comentario y el tiempo que le dedicaste al post !!! : D