Se irá comentando el desarrollo de una aplicación capaz de realizar análisis sintáctico y morfológico del castellano.
jueves, 24 de septiembre de 2015
Diccionario Genérico Distribuido por RMI
En entradas anteriores propuse rehacer la aplicación abstrayendo las estructuras generales para darles forma de servicios, actuando unas veces de servidor y otras de cliente.
Atendiendo simplemente a la estructura del autómata recordamos que estaba compuesto de un diccionario de nombres de las expresiones regulares, otro diccionario de las propias expresiones regulares junto con sus tablas de transiciones y estados finales, un grafo, etc. Del mismo modo el resto de la aplicación usaba distintas subestructuras de datos que en la versión actual se encuentran entrelazadas impidiendo muchas veces trabajar concurrentemente.
Para empezar a desarrollar la idea comencé con algo sencillo, genereralicé el diccionario de modo que ya no está restringido a Strings o a ER como antes. Además añadí la funcionalidad de modificación y borrado. En la versión anterior sólo se podían insertar elementos.
¿Porqué distribuir el diccionario?
En un primer momento la idea de distribución de la aplicación era simplemente asignar a varios pcs un servicio determinado, pero ¿cómo gestionar los servicios réplicas?, ¿recuperación de información?...
El punto de vista que ahora estoy llevando a cabo va más allá. El código de los diccionarios es el mismo en todos los pcs. En cada uno de ellos se ejecuta un gemelo, una versión del diccionario distribuido con su espacio de trabajo particular, es decir, su IP concreta de la máquina y su puerto. Los gemelos se comunican entre sí mediante notificaciones para estar todos actualizados en cuanto a cuando se ha añadido un nuevo gemelo, se va a eliminar o ha muerto, etc.
Cada gemelo gestiona una serie de servicios y réplicas, tanto en su pc como remótamente de otros gemelos.
Parece un poco lío, pero con un ejemplo es más fácil de ver:
El caso más sencillo es trabajar con un único pc. Este ejecuta un sólo gemelo del diccionario con IP ip1 puerto p1.
Hacemos que el gemelo lance un servicio diccionario. En él se irán insertando todas las cadenas y objetos remotos como si de un diccionario normal y corriente se tratase y trabajaríamos con él sin mayor complicación. Todo se ejecuta en la misma máquina.
Ahora podemos añadir una réplica en la misma máquina para poder recuperar datos en caso de que hubieran problemas con el servicio diccionario. Esto es, el gemelo lanza una réplica con la misma IP.
Podemos lanzar tantos servicios y réplicas en la misma máquina como queramos, aunque no tiene mucho sentido. Lo interesante es que los servicios y las réplicas se balancean automáticamente para intentar tener el mismo número de objetos almacenados en cada uno de ellos.
Como decía, en una misma máquina no tiene mucho sentido, pero ¿y si utilizamos más máquinas? Podemos ejecutar un gemelo en la máquina con IP ip1 y puerto p1 y que lance un servicio diccionario, y podemos ejecutar otro gemelo en la máquina con IP ip2 y puerto p2 y que lance una réplica. Esto significa que si tanto el gemelo 1 como el 2, por cualquier motivo cae, el otro es capaz de recuperarse de forma automática y transparente. Si conectamos más pcs y en estos se ejecuta su gemelo y éstos lanzan más servicios diccionarios y réplicas, se irán balanceando repartiéndose la carga facilitando su recuperación en caso de cualquier tipo de error.
Un dato importante a saber es que no hay un gemelo master ni nada por el estilo. Todos tienen la misma categoría, todos los servicios actúan como servidor y cliente a su vez, dependiendo de lo que pretendan hacer. Si un gemelo cae, los demás gemelos son capaces de recuperarse y seguir como si nada.
Se ha dotado de métodos sincronizados para la concurrencia en aquellos que se ejecutan sin mucha complejidad y son rápidos. Sin embargo, los métodos más pesados como pueden ser el de redimensionar, el de comprobar la integridad, recuperar, entre otros, antes de lanzar el método un gemelo determinado, éste lanza una notificación al resto de los gemelos para que se queden bloqueados a nivel de usuario (más adelante explico), ejecuta el método sabiendo que no será molestado con intrusiones de métodos lanzados por otros y vuelve a lanzar la notificación para que se desbloqueen y puedan continuar.
Aunque el propósito es que sea un único sistema que trabaje con varias estructuras de este tipo de modo que todo esté organizado por éste, con la sincronización y bloqueos podemos manipular el diccionario distribuido a través de todos los gemelos a la vez. El sistema cuando tenga que trabajar con el diccionario no le importará con qué gemelo hacerlo, ya que para éste, los resultados son los mismos.
Para lograr esto el código se ha fraccionado en cuatro niveles:
Nivel 0: Se trata de la implementación de las celdas, manipulador y diccionario genérico. El manipulador es para facilitar el uso de un pivote individual por hilo y hacer posible la concurrencia.
Nivel 1: Se implementan el interfaz, remoto, cliente y servidor de un servicio diccionario.
Nivel 2: Se implementan servicios y controlServicios, que gestionan los servicios y réplicas que se ejecutan en su máquina.
Nivel 3: Gestionan los servicios y réplicas de una misma máquina con las demás.
Nivel 4: Nivel usuario, tiene implementadas las interfaces, remotos, clientes y servidores para que puedan lanzarse notifiaciones entre los gemelos y ejecutar los métodos indicados por el usuario y de forma transparente se ejecuten en la misma máquina o remótamente según donde se encuentren los datos determinados. Cuando se lanza el bloqueo es este nivel el que se queda inactivo pudiendo trabajar los otros tres niveles independientemente.
Para que esto funcione, todos los gemelos deben de saber de la existencia del resto de gemelos y de todos los servicios y réplicas que han sido lanzados y por quién. Esto está organizado en dos listas que se van sincronizando todos, de este modo, se garantiza la autonomía.
¿Está terminada la implementación?
En el punto actual funciona perfectamente en una sola máquina lanzando todos los servicios y réplicas que queramos. Ahora estoy modificando algunos puntos para hacerlo funcionar con más máquinas. En un principio daba problemas para que pudiera registrar servicios de forma remota. RMIRegistry sólo registra objetos en su misma máquina. Comentar que cada máquina trabaja con su rmiregistry local. Esto hoy mismo ha quedado solucionado. Estoy con la sincronización de las listas de gemelos y servicios-réplicas.
Lo siguiente es la autorecuperación. Ahora mismo se recupera pero de forma manual, es decir, mato yo mismo un servicio y lanzo el método de recuperación y lo hace perfectamente. Lo que falta es que detecte que ha caído un servicio, réplica o gemelo y lo recupere todo sin intervención del usuario/coordinador.
Suscribirse a:
Enviar comentarios (Atom)
Como los gemelos son independientes han de tener toda la información del Diccionario Distribuido cada uno de ellos. Esto quiere decir que cada gemelo ha de saber de la existencia de los demás gemelos y de todos los servicios-réplicas que tiene lanzados cada uno.
ResponderEliminarEsto se consigue mediante notificaciones al producirse un cambio al insertar/eliminar un servicio-registro, o al insertar/eliminar gemelos.
Los servicios-registros, aunque sepan de la existencia todos, sólo se ejecutan en su máquina con la que fue lanzada, con lo que será su gemelo quien la gestione.
Si por ejemplo, en un servicio se almacenan las cadenas que van desde la a a la j y en otro servicio desde la j a la z, los gemelos sabrán localizar el servicio que ha de insertar una nueva cadena. Para hacer esto los gemelos han de saber cual es su primero y último de cada servicio.
En este momento al insertar un nuevo gemelo y servicicios-registros, el resto del sistema es notificado y actualizan sus listas.
Lo que falta es que actualicen sus primeros y últimos. En este momento si se inserta una cadena en un servicio en una máquina, aparece reflejado en las dos máquinas, sin embargo, no actualiza el primero/ultimo en el segundo servicio. Esta información es primorcial, como decía, para poder localizar la posición y el servicio a la hora de insertar otra cadena.
Para resolver esto estoy programando que cuando un servicio/réplica inserte una cadena y ésta pase a ser la primera o última, lanzará una notificación a todos los gemelos para que actualicen sus listas de servicios/réplicas.
Aún no se autorecupera, pero sí que inserta cadenas indistintamente del gemelo con el que lo haga, los cambios aparecen en todas a la vez, con lo que el objetivo de 'distribución' y 'transpariencia' se ha logrado con creces.