Jun 16

Comunicación Consola Servidor : Hazelcast, Vert.x, REST

Logo HazelcastEn el mismo proyecto del post anterior, estaba replanteandome como hacer que la interfaz de usuario (consola, aplicación java de escritorio) se conecte con el servidor (ejecutable java que no es servidor web ni de aplicaciones). Habitualmente abrimos un socket TCP/IP y definimos la mensajería. Tenemos librerías ya preparadas para esto, es eficiente y lleva muchos años funcionando. Pero a todos nos gusta jugar. Para este proyecto, me estoy planteando usar algún otro tipo de comunicación.

En otros proyectos que he visto (uno de los cuales lo he heredado), usan servicios web REST para todo lo que sea petición/respuesta desde la consola al servidor. Cuando el servidor quiere enviar algo de motu propio a todas las consolas, se usa ActiveMQ como cola de mensajes. Como el proyecto está con Spring Framework, hay soporte para las dos cosas haciendo fácil su codificación. Este mecanismo también está funcionando sin problemas.

Pero lo dicho, quiero seguir jugando.

Lo del servicio REST … ¿podemos eliminarlo usando sólo colas de mensajes o algo parecido? ¿Y en vez de una cola de mensajes ActiveMQ podemos usar algo más sencillo y ligero?

Pues me plantee hacerlo primero todo con Hazelcast. Si pongo modelos compartidos entre consola y servidor y con el mecanismo que tiene Hazelcast de publicación/suscripción a base de topics, quizás me bastaría sólo Hazelcast para reemplazar los web services REST y a ActiveMQ.

Pues no. Por un lado, Hazelcast no ofrece un mecanismo de petición/respuesta. Si uso un topic como petición de forma que el servidor se suscribe a ese topic de petición y cuando la consola quiere algo, publica en ese topic qué quiere, el servidor no tiene forma de responder. Habría que crear un topic temporal de respuesta, de forma que la consola que ha hecho la petición se suscribe a ese topic temporal y recoge la respuesta. Esto lo ofrece ActiveMQ de forma transparente, pero con Hazelcast habría que currárselo.

Y me surgió un segundo tema. Si en base de datos tenemos guardadas una serie de cosas y la consola las quiere, lo normal es pedírselas al servidor y el servidor en respuesta las devuelve. ¿Cómo lo hace en el caso de Hazelcast? Tendría que meterlo en algún tipo de colección de Hazelcast en memoria. ¿Y qué pasa sin son muchos datos?. Pues que hay que cargarlos todos en memoria, o inventar un mecanismo de cargas parciales, con paginados, etc, etc.

Así que descarté el uso exclusivo de Hazelcast. Lo he dejado, pero principalmente porque me vale para el modelo “vivo” de datos. En la aplicación se verá un mapa con barcos moviéndose. Estos barcos moviéndose en lo que llamo modelo “vivo” de datos, los datos que actualmente se presentan en todas las consolas y que el servidor se encarga de ir actualizando para todas. Esto es una ventaja frente a lo que teníamos. Sin Hazelcast (o un equivalente), el servidor tenía que enviar todos los cambios de ese modelo “vivo” a través de eventos por socket TCP/IP o ActiveMQ y las consolas debías actualizar su copia de dicho modelo en base a esos eventos. Con Hazelcast, el servidor toca la colección compartida y la consola ve dicha colección actualizada en todo momento. Es Hazelcast el que se “come” por debajo todo el tema de eventos de cambios del modelo y la actualización en el lado de la consola. La consola puede suscribirse a cambios en esa colección compartida, hazelcast ofrece esa posibilidad, pero para hacer otras cosas que necesite, como borrar el icono del barco en el mapa o desplazarlo, pero no para actualizar esa misma colección de barcos que ya está actualizada

Bien, no me vale Hazelcast para todo. ¿Qué tal Vert.x?. Tiene un EventBus, más sencillo de manejar que ActiveMQ, contempla la petición/respuesta, usa (o puede usar) Hazelcast por debajo por lo que encaja con el Hazelcast que me he planteado usar. Así que eso me planteé. Dejo Hazelcast para el modelo vivo y usar EventBus de Vert.x para peticion/respuesta y publicación/suscripción con Topics.

Me pongo a hacer mis pruebas y … ¡¡Sorpresa!!. Vert.x no garantiza el orden en la entrega de los eventos. Si garantiza que si un publicador envía eventos, un suscriptor concreto los recibe en el orden que el publicador los ha enviado. Pero no garantiza nada más. Si un publicador publica en varios topic y cada topic tiene un suscriptor distinto, los suscriptores pueden recibir los eventos en cualquier orden, no necesariamente en el orden en que el publicador los ha enviado. Y eso me hizo polvo. Para petición/respuesta a veces se necesita que las cosas vayan en orden, que no me contesten antes a lo que he pedido después. Así que que para petición/respuesta descarté Vert.x, seguro que puedo hacer apaños para garantizar el orden, pero va en contra de la filosofía de Vert.x y me parece una fuente importante de bugs si los desarrolladores se despistan.

¿Y para publicación/suscripción con topics, sin respuesta?. Bien, tengo Hazelcast que también lo hace. No voy a meter un framework por encima sólo para eso. Lo dejo aparcado por si en un momento dado Hazelcast se me queda cojo en este asunto y Vert.x me lo solucionara, pero lo dicho, queda aparcado.

¿Cómo hago al final lo de petición/respuesta?. Pues al final he dejado los web services REST.

¿Qué creo que he ganado con todo este cambio aparte de jugar y aprender? Principalmente el tema del modelo “vivo”. La colección compartida de Hazelcast entre consola y servidor me ahorra enviar eventos de cambios en ese modelo en el servidor, recogerlos en la consola y reconstruir en consola con código el mismo modelo que tiene el servidor.

En cuanto a topics, AciveMQ frente a Hazelcast, desde luego es mucho más potente y con más posibilidades ActiveMQ, pero es más complejo y no necesito todas esas posibilidades, tiro de momento por la opción más simple de Hazelcast.

 

May 16

ESB vs MQ vs DDS

Ando últimamente mirando herramientas estilo Enterprise Service Bus (ESB), de colas de Mensajes (MQ) y Data Distribution Systems (DDS). La primera vez que oyes hablar de ellas te preguntas ¿qué son exactamente? ¿Para que sirven?. La primera explicación que encuentras en la Web de cualquiera de ellas es la habitual : "Una herramienta maravillosa que con poco trabajo te resuelve todos tus problemas y te va a hacer el más feliz de los programadores". Si profundizas un poco más, llegas a las definiciones técnicas que nadie entiende, como "Es la implementación del estándar JSR x33, bájate y lee el estándar de dos mil páginas para saber de qué estamos hablando".

Aquí va un "pequeño" resumen de qué son y para qué sirven, según lo he entendido tras andar investigando y jugando con algunas de ellas.

¿Cual es el problema que pretenden resolver?

Todas ellas pretenden resolver más o menos el mismo problema, pero lo hacen con diferentes aproximaciones, siendo más o menos intrusivas en nuestro desarrollo.

Imagina un sistema complejo en el que varios ejecutables están corriendo en varios ordenadores. Estos ejecutables colaboran entre sí enviándose mensajes unos a otros, compartiendo datos, repartiendo tareas, etc. El esquema podría ser similar al de la siguiente figura, donde hay tres ordenadores (las cajas cuadradas), con siete ejecutables A,B,C… y las comunicaciones entre ellos

Problema que pretende resolver ESB, MQ y DDS

Cada ejecutable conoce a los demás ejecutables con los que está trabajando y establece las conexiones que necesita para colaborar con ellos. Esta aproximación tiene varios problemas:

  • Cada ejecutable debe saber las IP/hosts y puertos donde están los otros ejecutables con los que colabora
  • Los ejecutables deben ponerse de acuerdo en el formato en el que se envían los datos.
  • Si más adelante queremos ampliar o modificar el sistema añadiendo, quitando o moviendo de sitio ejecutables, tendremos que tocar todos los ejecutables afectados. Por ejemplo, imagina que añadimos un cuarto servidor con ejecutables H y J que quieren colaborar con algunos de los ya existentes, casi seguro que tendremos que tocar los ya existentes para que admitan a los nuevos.
  • ¿Y qué pasa si alguno de esos ejecutables existentes no podemos tocarlo porque es un producto comercial, nos lo hizo otra empresa o se ha ido el desarrollador que lo concoce? (triste, pero real como la vida misma, a veces se va el que lo ha hecho y nadie más se atreve a tocarlo).

Dar flexibilidad a este tipo de montajes y facilitar la conexión con nuevos ejecutables es lo que pretenden resolver todo este tipo de herramientas ESB, MQ y DDS. Empezamos a ver la solución que propone cada una de ellas, empezando de la menos intrusiva (la que nos oblgaría a hacer menos cambios en nuestros ejecutables ya existentes) a la más intrusiva (que nos obligaría prácticamente a diseñar el sistema pensando en ella).

Enterprise Service Bus (ESB)

 Hay varias herramientas gratuitas de este tipo, como Apache Service Mix, Mule ESB, JBoss Fuse, WSO2 Enterprise Service Bus.La solución que proponen estas herramientas es arrancar uno o varios ejecutables ESB de forma que todas las aplicaciones se conecten al ESB en vez de unas con otras. Es en este ESB donde configuramos las rutas de las conexiones, diciendo por ejemplo, que lo que mande el ejecutable A se debe enviar al ejecutable B. El esquema sería parecido al siguiente

Enterprise Service Bus

Las bondades de esta solución son las siguientes

  • En el ESB se establecen las rutas de qué ejecutables hablan con qué ejecutables. De esta forma, el conocimiento de qué ejecutables hay y dónde están queda centralizado en el ESB, en vez de repartido por los distintos ejecutables de nuestra aplicación.
  • Estas rutas se pueden crear, modificar y eliminar en caliente, por lo que sería posible, por ejemplo, añadir un nuevo ejecutable a nuestro sistema y establecer las rutas en caliente para que este ejecutable sea capaz de enviar/recibir de los que ya están arrancados.
  • Las rutas pueden tener "inteligencia", de forma que pueden enviar un mensaje procedente de un ejecutable a varios ejecutables a piñón fijo, o bien seguir algún tipo de criterio para decidir a cual enviarlo, filtrar para enviar unos mensajes sí y otros no, etc, etc. Por supuesto, esta "inteligencia" se puede cambiar en caliente.
  • Como parte de la "inteligencia", las rutas pueden modificar los mensajes, siendo uno de los casos más habituales el cambio de formato de mensaje. Si un ejecutable usa el protocolo FTP y el otro HTTP, la ruta podría hacer la conversión correspondiente para, por ejemplo, coger ficheros de un servidor FTP y subirlos a una web con HTTP o al revés. Si los protocolos son estándar (FTP, HTTP, SNMP, XML, …) los ESB saben tratarlos y convertir entre ellos. Si no son estándar, podemos añadir nuestro trozo de código dentro del ESB para hacer las conversiones a nuestro gusto.
  • Otra ventaja adicional es que estas herramientas permiten de alguna forma monitorizar las rutas, de forma que podemos ver qué mensajes pasan por ellas, si se utilizan o no, si hay fallos, etc.
  • Las posibilidades que implementan las rutas suelen conocerse como "Enterprise Integration Patterns" o EIP. Siguiendo el enlace puedes ver todos estos patrones que suelen implementar.

Este es el mecanismo menos intrusivo porque nuestros ejecutables siguen siendo tal cual y enviando/recibiendo los mensajes en los formatos que ya tienen. Únicamente debemos cambiar la IP/host de conexión para que sea contra ESB en vez de unos con otros y nos queda el trabajo de configurar rutas en el ESB y darles la lógica que sea necesaria (conversiones de datos, filtros y en general cualquier EIP de la lista anterior).

Colas de Mensajes MQ

 Hay varias herramientas de colas de mensajes gratuitas, siendo ActiveMQ posiblemente la más conocida. Otras son RabbitMQ o Apache Kafka. La solución planteada es similar a ESB en el sentido de que debemos arrancar uno o más ejecutables MQ y nuestras aplicaciones deben conectarse a ellos. Sin embargo, es más intrusiva que ESB en el sentido de que debemos conectarnos con el protocolo que establece MQ, no podemos seguir conectándonos usando nuestros propios protocolos.

A MQ le enviamos mensajes en el formato que admite y cualquier otro ejecutable que tenga interés puede recibirlos. El esquema de trabajo sería como el siguiente

 Message Queue

Dentro de MQ se definen "topic" y "queue" con nombres, tantos como queramos. Los ejecutables envían mensajes a estos topic o queue. Otros ejecutables se suscriben a estos topic y queue de forma que recibirán los mensajes. La diferencia entre un topic y un queue es que un topic entregará sus mensajes a todos los ejecutables que se hayan suscrito, mientras que un queue sólo lo entregará a uno de los suscriptores. Un queue se utiliza más como un reparto de tareas.

Esta aproximación me parece más sencilla y elegante que la de los ESB, pero tiene el inconveniente de que nuestros ejecutables deben estar pensados para enviar y recibir mensajes a MQ, por lo que puede ser un trabajo modificar un ejecutable existente para adaptarse a MQ.

O quizás no. Los ESB suelen entender los mensajes de los MQ, de hecho, Apache Service Mix (un ESB) lleva integrado ActiveMQ (un MQ) Nuestra aplicación puede conectarse con su protocolo normal contra ESB y en ESB podemos establecer una ruta para convertir los mensajes en el formato de la aplicación a mensajes en el formato de MQ y enviarlos a MQ. Por otro lado, en otro sitio, haremos una ruta que recoja estos mensajes MQ, los convierta al protocolo que necesite la aplicación receptora y se los despache en el formato que ella entienda.

 Data Distribution Service DDS

De este tipo de aplicaciones hay menos gratuitas, pero tenemos por ejemplo OpenDDS y OpenSplice DDS. El concepto aquí es similar a las MQ, pero va más allá. Debemos arrancar uno o más ejecutables DDS y nuestras aplicaciones deben conectarse a ellos. La diferencia con MQ es que en DDS debemos definir todos nuestros tipos de datos y nuestros datos. Por ejemplo, si nuestra aplicación controla una flota de camiones, debemos indicar a DDS que tenemos un tipo camión con los campos matrícula, carga máxima, año de fabricación, modelo, marca, etc. y debemos indicar a DDS que vamos a tener una lista de camiones en revisión, una lista de camiones en recorrido, etc, etc.

A partir de aquí, nuestros ejecutables se conectan a DDS y en formato DDS van alimentando estos datos. Cualquier ejecutable puede a su vez suscribirse a los datos para enterarse de cambios en ellos. El esquema sería el siguiente

Data Distribution Service DDS

Esta solución como se puede intuir es la más intrusiva de todas, ya que no solo nos obliga a adaptarnos al protocolo de DDS, sino que de alguna forma dejamos en DDS la responsabilidad de mantener nuestros datos. Hasta cierto punto, DDS hace las veces de una base de datos (de hecho, suele tener una base de datos detrás que respalda nuestros datos), pero con la posibilidad de suscribirse a cambios. DDS suele además poder trabajar "distribuido". Varios ejecutables DDS interconectados entre sí, de forma que si metemos datos por uno de ellos, podemos retirarlo en cualquiera de los otros.

La principal ventaja de DDS es que está pensado para ser muy eficiente, por lo que no es raro encontrarlo en sistemas de tiempo real, defensa , espacio, transporte.

May 09

He leído “ActiveMQ in Action”

ActiveMQ in actionHe leído ActiveMQ in Action, una forma de enterarse de qué va ActiveMQ. El libro en sí no me ha gustado demasiado, se hace pesado de leer. Explica un concepto (eso sí es útil), te dice que el ejemplo de código de ese concepto está en tal sitio web, que para ejecutarlo uses tal comando y luego te planta dos páginas de libro viendo el log de salida del programa. Así una y otra vez.

Sin embargo, como yo soy más de enterarme bien de los conceptos que de andar mirando el detalle del código, el libro me ha servido para saber de qué va ActiveMQ y sus posibilidades.

ActiveMQ ofrece un servicio de mensajería entre distintas aplicaciones, o incluso para distintas partes dentro de una misma aplicación. Una aplicación o una parte de ella puede enviar mensajes a ActiveMQ y otra aplicación u otra parte de la misma pueden recoger esos mensajes. Aparte de esto, las características que mas me han llamado la atención de ActiveMQ son las siguientes:

Los mensajes pueden ser persistentes, es decir, se guardan automáticamente en ficheros o base de datos, según configuremos, de forma que si paramos la aplicación ActiveMQ y la volvemos a arrancar más adelante, los mensajes no entregados cuando paramos la aplicación, se recuperan y se pueden entregar en cuanto alguien los recoja.

Tiene posibilidad de arrancar varios ActiveMQ para trabajar colaborando, bien en forma de cluster, bien en configuración maestro-esclavos. En la configuración de cluster, los ActiveMQ se interconectan entre sí, de forma que si entregamos un mensaje en uno de ellos, se puede recoger en cualquiera de los otros. La configuración de cluster es muy sencilla, tiene posibilidad de "autodescubrimiento" de forma que según se vayan arrancando aplicaciones ActiveMQ en la red local se incorporan automáticamente al clustter, o bien podemos indicar las IPs concretas donde está cada ActiveMQ, para formar un cluster fijo. En cuanto a la configuración maestro-esclavo, es también sencilla. Un ActiveMQ es el que hace de maestro y es al que se conectan las aplicaciones. Los esclavos únicamente van guardando réplicas de los mensajes. Si el maestro se cae, uno de los esclavos entra en funcionamiento y las aplicaciones deben re-conectarse al nuevo maestro. Las librerías para clientes de ActiveMQ se pueden configurar de forma que hagan esta reconexión automáticamente.

Y lo que más me ha gustado, embeber un ActiveMQ dentro de nuestra propia aplicación es casi inmediato. Si ponemos la dependencia del jar adecuado (activemq-broker y activemq-client), basta con que nuestra aplicación establezca una conexión contra ActiveMQ usando el protocolo "vm://un-nombre" y automáticamente se creará una instancia de ActiveMQ con nombre "un-nombre" dentro de nuestra aplicación, disponible para cualquier parte de ella. Si además, a esa instancia le creamos una conexión adicional  "tcp:….", otros programas externos podrán conectarse a nosotros para recibir nuestros mensajes o enviarnos los suyos propios. También podemos crearle conexiones de red adicionales, de forma que nuestra instancia embebida de ActiveMQ forme parte de un cluster externo de ActiveMQ.

En resumen, me ha gustado la herramienta, es bastante simple de poner en marcha, de montar clusters e incluso de embeberla en nuestras aplicaciones. No me ha gustado que el envío de mensajes es relativamente tedioso: hay que crear la conexión, de la conexión una sesión, de la sesión un destino que no es más que un nombre, del destino crear una cola o un topic (son dos posibles formas de distribución del mensaje), un producer (el que envía el mensaje), construir el mensaje y enviarlo. Muchos de estos pasos se pueden dejar preparados para no repetirlos muchas veces, pero no deja de ser "un pelín" engorroso. Con la recepción algo similar.

De todo esto han salido algunos tutoriales de ActiveMQ en la Chuwiki.