Asignación de un líder Zookeeper

Una vez entendido la teoría y llevado a cabo la instalación de este otro componente llevaremos a la práctica los conocimientos de este componente, y que mejor ejemplo que programando una elección de un nodo maestro cuando tenemos una aplicación distribuida y nuestro líder cae, ejemplos, (namenode, resourcer manager, hmaster, etc). El ejemplo anterior que vimos manualmente en manejo de Apache Zookeeper ahora procederemos a verlo de forma programática, este ejemplo tiene muchas aplicaciones y más para cuando se trata de aplicaciones distribuidas y el monitoreo de las mismas.

Procederemos por iniciar el servidor de zookeeper, en este caso como solo cuento con un nodo con este clúster de prueba la aplicaciones que mostraré en este blog hace uso de los threads (hilos) para simular que tengo N clientes que se conectan a este servicio de zookeeper.

El código se encuentra en: https://github.com/NeoChoosenOne/ZookeeperLeaderElection


Una vez que ya inicializamos el servicio de zookeeper como muestra la siguiente imagen y tenemos el código descargado, procederemos al uso de este mismo.


Bueno ahora utilizando el jar con nombre zookeeperelection-0.0.1-SNAPSHOT-jar-with-dependencies procederemos a realizar la prueba de elección de líder, para poner en acción el jar necesitamos ejecutarlo de la siguiente manera.

java -jar zookeeperelection-0.0.1-SNAPSHOT-jar-with-dependencies id host:port  

Procederé por ejecutar una instancia de este jar como se muestra en la siguiente imagen.

Cómo podemos ver imprime un mensaje auto nombrándose líder ya que es la primera instancia y por ende el único que esta realizando una acción, también podemos notar que el path para este nodo que se ha creado del tipo sequential es /election/p_0000000000, realizamos este mismo paso en una consola diferente, el resultado ahora lucirá así.

Como podemos ver, ahora el nodo ha tomado el valor del path como /election/p_000000001 y aparte ha programado un watch en el nodo /election/p_0000000000, el cuál se disparará cuando realice una modificación o se elimine el nodo.

Por último abrimos una tercera ventana y realizamos lo mismo que los dos pasos anteriores.


Cómo podemos ver ha realizado las mismas acciones que el paso anterior, ahora tenemos un total de 3 secuencial nodes, de los cuales uno es el líder, ahora, lo interesante de este ejemplo es, ¿Qué pasaría si el líder se cae?, osea el nodo /election/p_0000000000, ahora imaginemos que a este nodo nosotros le damos el rol de que es el líder de nuestra aplicación distribuida como el namenode, resourcemanager, etc, ó nuestro servidor de aplicaciones principal, pero por cuestiones del universo sabemos que estos pueden fallar, nos gustaría tener un líder  en standby que tome el lugar del anterior, ¿Cierto?, y nos brinde alta disponibilidad, bueno este ejemplo da las bases para eso, por que ahora procederemos a matar el proceso de nuestro nodo líder en la consola y debería lucir algo así.


Oh no!!! nuestro líder ha caído, pero como en todo debe levantarse uno nuevo para tomar su lugar y sustituirlo y eso es lo que hará el nodo con id 2 que tenemos en otra consola el cuál luce así.

Como podemos ver este nodo que estaba en espera ahora ya es el líder y el anterior líder ha muerto, el lugar ahora lo tiene el nodo con id 2 y el nodo con id 1 se encuentra muerto, ahora de los 3 nodos que teníamos anteriormente  ahora contamos con 2, pero seguimos contando con un líder gracias a esta poderosa herramienta.

Y, ¿Qué pasaría si otra vez se cae el nodo líder?, bueno afortunadamente tenemos otro nodo en espera a tomar su lugar.

Y tal vez ahora se pregunten, ¿Qué pasaría si en lugar de caerse un líder se hubiera caído cualquier otro nodo?, por más trivial que parezca la respuesta, no pasaría nada, más que ahora tendríamos n-1 candidatos para sustituir al líder en caso de que llegará a fallar.

Bueno pero ahora expliquemos la estructura abstracta de este proceso.

Contamos con 4 elementos o nodos en nuestro conjunto.

Prácticamente los últimos tres nodos son hijos de el elemento o nodo /election

Si se eliminará el nodo /election toda la información se perdería de los 3 nodos hijos y ellos mismo ya no existirían.

Bueno estas estructuras pueden ser tan complejas como se quieran, pero al final tiene estructura de grafo. Entonces podemos definir más generalmente a todo el espacio del trabajo del Zookeeeper como un conjunto de Grafos. Empezaremos por definir que es un grafo.

Una vez teniendo definida esta herramienta, empecemos a hacer la comparación del espacio de trabajo con estas estructuras.

Como zookeeper trabaja con nodos, podemos tomar cada nodo como un vértice, como vimos en la teoría tanto los nodos como los znodes pueden tener hijos, entonces para ejemplificar este caso de elección de líder definiré algunas propiedades de grafos para que tenga sentido.


Cómo podemos notar nosotros que para este ejemplo de elección de líder tenemos un grafo lineal dinámico, con una ruta finita.

Para poder definir un grafo lineal necesitamos definir el Isomorfismo entre grafos, el cuál se define de la siguiente manera.


Una vez definido el isomorfismo entre grafos necesitamos una herramienta más y es la de definir un paseo y sus características, ya que la elección de líder genera un grafo, con un paseo entre sus znodes y el nodo inicial es el que estamos utilizando como nodo padre de los ephemeral znodes el cuál llamamos  /election.


Como complemento de las definiciones anteriores, necesitamos una característica importante de un grafo en el cual existe un paseo y es la de un grafo conexo, la cuál veremos a continuación.



Por último daré la definición de un grafo lineal y con esto empezamos a utilizar las herramientas anteriores para describir el modelado de la elección de líder de este código.






Con esto podemos empezar a trabajar ahora si, como mostré en las primeras imágenes, este ejemplo de elección de líder genera un grafo, y más aún genera un grafo lineal de acuerdo a nuestra definición anterior de grafo lineal, ya que la forma que se genera gracias a su nodo election por cada uno de sus hijos los ephemeral znodes lo hacen isomorfo al grafo de la imagen anterior obviamente con menos nodos, para recordar agregaré la imagen a continuación.



Ahora con la imagen anterior definiremos un paseo entre esta estructura, recordare que los znodes son equivalentes a tantos servidores tengamos en nuestro ensamblado del zookeeper, así que habrá tantos ephemeral znodes tantos servidores haya, como para este ejercicio utilizo clases para ejecutar hilos entonces simula n servidores, donde n es en base a los recursos de la máquina, para este ejemplo considera 4 nodos y 3 znodes pero funcionara de acuerdo a toda la teoría anterior para n znodes.

Ahora con los nodos anteriores podemos ver cada uno como un vértice y más aún es un grafo lineal si lo definimos de la siguiente manera.


Bueno, ahora también del grafo anterior podemos decir que es conexo por que podemos definir un paseo y más que paseo definiríamos un camino de la siguiente manera, {election,p_0000000000,p_0000000001,p_0000000002}, y gracias a esto tenemos una representación matemática de nuestro programa, pero falta lo más importante, como es que se selecciona el líder en este grafo.

A partir de aquí trabajaré con la definición de grafo lineal y tomaré como base el grafo lineal trivial para ejemplificar como es que trabaja el programa. Para eso mostraré gráficamente como es que cada vértice a partir del tercer elemento del camino va insertando un watch en i-1 donde i es el vértice en cuestión del camino recorrido con i > 2.


Entonces como podemos ver, cuando es creador el vértice 2 o en el caso de computación el znode p_0000000000, el nodo no crea ningún elemento watch, pero cuando es creado el elemento siguiente p_0000000001 aparte de agregarle un nuevo vértice al grafo, una propiedad de ese vértice es inyectar un watch en el vértice anterior a el, el cuál estará verificando la salud del vértice anterior a él.

Gracias a esta propiedad del znode, es posible definir la dirección del camino que ya hemos definido el cuál tendrá la siguiente forma.


Entonces como podemos ver, con esto termina nuestro modelo abstracto del programa, ya que si el vértice (znode) p_0000000001 llegará a morir, lo que implica que desaparecería del grafo, el vértice(znode) p_0000000002 lo sabría y entonces ahora el vértice  p_0000000001  desaparecería quedando el siguiente grafo.

Cómo podemos ver ahora tenemos un grafo de orden menor, y el único nodo disponible para sustituir al líder es el p_0000000002, si el nodo p_0000000002 llegará a morir, no existe vértice en el grafo que lo sustituya quedando un grafo nuevo de orden aún menor que el de la imagen anterior y quedando solo el líder disponible, también podemos agregar nuevos vértices para que sustituyan las ausencias, pero trabajar con grafos dinámicos no es común para casos en la vida real.

A manera de conclusión el zookeeper es una herramienta muy poderosa cuando se trata del desarrollo para aplicaciones que cumplan con el requerimiento de alta disponibilidad, y ya que esta es una característica que hoy en día es muy importante para las aplicaciones en un ambiente productivo, hoy en día un clúster productivo con todos los componentes de Apache genera de manera abstracta un grafo bipartito completo, hoy en día el Apache Zookeeper es el componte base de todo clúster y todas las aplicaciones de Apache de manera distribuida.  

Comentarios

Entradas más populares de este blog

Manejo Apache Hive

Replicación y Formas de Paralelización Apache Hadoop