Archivo
Zipkin
Zipkin es un gestor de traza o de log distribuido. Es muy apropiado para entornos en los que los componentes se encuentran separados en diferentes componentes de red o en entornos de ejecución distintos, como pueden ser las arquitecturas de microservicios.

La información de traza es recogida por diferentes librerías de Zipkin que añaden «instrumentación» a nuestro componente software. Estas librerías se encuentran disponibles para C#, Go, Java, Javascript, Ruby y Scala (todas estas soportadas por el propio proyecto OpenZipkin, aunque la comunidad aporta muchas más). Estas librerías se encargan de recoger la información de traza en cada operación y añadir la mínima información de identificación que permita correlar las trazas en cada componente de nuestro sistema y ver la operación como un todo, como una única traza de la misma pasando por cada uno de los componentes.

Esta información de traza es remitida por los Reporters al Collector centralizado de Zipkin, que la valida y almacena. Zipkin permite la explotación de esta información mediante una API propia o mediante la UI que ataca a esta API (que puedes ver en la imagen).
Puedes encontrar más detalles de la arquitectura de Zipkin aquí.
Las imágenes de esta entrada se han obtenido del propio proyecto de Zipkin.
Apache Kafka
Apache Kafka es un sistema de procesado de mensajes. Los mensajes se publican por los productores (producers) en colas denominadas topics y se consumen de la cola por los consumidores (consumers) suscritos a dicha cola. Es una solución al problema de los productores-consumidores concurrentes. En este problema existen uno o más actores (productores) que generan los mensajes que son procesados por uno o más actores (consumidores) de manera concurrente.
El sistema de encolado y procesado de mensajes es distribuido y particionado en diferentes instancias denominadas brokers que conforman el clúster de Kafka. Dentro del clúster, en cada broker, cada topic o cola está dividida en particiones en las que los mensajes se almacenan de manera secuencial. Las particiones de un topic permiten:
- Distribuir la carga de trabajo entre diferentes brokers y consumidores
- Tener tolerancia a errores de los brokers, al poder tener replicadas la misma partición en brokers distintos (aunque solo una es la activa, pueden tomar el sitio de la activa si ésta cae)
El productor es quien encola los mensajes en cada partición, que mantiene estos mensajes de manera secuencial y ordenada. Es el propio productor quien decide en qué partición específica del topic se almacena el mensaje. De esta manera el balanceado de los mensajes en las diferentes particiones queda en manos del productor, no del clúster, que puede basarse en criterios de balanceado de carga o de lógica de negocio.
El modo en que se distribuyen los mensajes a los consumidores es también bastante flexible. Los consumidores se agrupan en grupos de consumidores. El clúster distribuye los mensajes de cada partición a un único consumidor del grupo, pero si hubiese más de un grupo de consumidores lo haría para cada grupo. De esta manera nos encontramos en un cola clásica con la carga balanceada entre varios consumidores si solo hay un grupo, y en un modelo de suscripción si tuviésemos más de un grupo.
El único modo de garantizar que los mensajes se consumen en el orden en que se crean es teniendo una única partición por topic y un solo consumidor por grupo. El único modo de garantizar que los mensajes se consumen una sola vez es teniendo un único grupo de consumidores.
El consumidor de cada partición es el que decide el orden en que se procesan los mensajes y los mensajes que se procesan. El consumidor tiene libertad para moverse en la partición accediendo a los mensajes de manera indexada (offset desde comienzo) y en sentido y con el criterio que quiera, y es quién lleva constancia de si han sido consumidos o no. Esto permite que la política o lógica de procesado de mensajes sea mucho más flexible que la cola tradicional, permitiendo el reprocesado de mensajes, el procesado en cualquier orden o el consumo de mensajes en la misma partición por diferentes consumidores (si están en distintos grupos).
Los mensajes se mantienen en la partición, con independencia de si han sido consumidos o no, durante el tiempo indicado por la política de retención de mensajes. No hay borrado de los mensajes. Esto se debe a una decisión de diseño muy eficiente, puesto que el acceso al disco secundario es siempre secuencial y no aleatorio, con las ventajas de rendimiento que supone.
Mi opinión es que Apache Kafka es un solución muy versátil y sencilla para consumir o transformar elementos de información que se generan a un ritmo no constante o mantenido, sino poissoniano, como son eventos, mensajes, peticiones… Siguiendo la documentación, que por cierto es muy buena y completa, los ejemplos de aplicación más comunes son como sistema de mensajería tradicional, monitor de actividad web o monitor de métrica de operaciones, agregador de log, gestor de eventos, procesador de streams de datos…
Apache ZooKeeper
Apache ZooKeeper es un servicio de datos en estructura de árbol. El servicio es centralizado, replicado, de alta disponibilidad y escalable; y garantiza la integridad en accesos concurrentes y el orden de modificación de los datos de manera secuencial. El sistema está formado por un conjunto distribuido de instancias denominado quorum que sirven la misma información.
El servicio es centralizado porque solo una de las instancias en ejecución hace las veces de instancia maestra, es la única que puede modificar los datos y que los replica al resto de las instancias. Esto garantiza el orden de los cambios, la atomicidad transaccional y la integridad concurrente de la información. Cuando la instancia maestra cae, el algoritmo de elección de líder, decide qué instancia es la nueva instancia maestra. El acceso de lectura, sin embargo, puede hacerse a cualquiera de las instancias levantadas del sistema. Esto por un lado garantiza la disponibilidad, al poder cualquier nodo convertirse en líder, y el rendimiento y la escabilidad, al permitir poder añadir todas las instancias que queramos al quorum para repartir la carga. Este diseño lo hace más adecuado para situaciones en que la lectura de datos es mucho más habitual que la escritura (al menos 1:10 según la documentación).
El servicio es rápido porque los datos se encuentran en memoria de cada instancia y solo se replican a disco para garantizar su persistencia. También es muy sencillo, tanto en el modo en que almacena la información; como en el modo en que se accede a la misma, con un conjunto de operaciones que son muy básicas y reducidas en número.
La forma en que se estructuran, organizan y acceden los conjuntos de datos es en estructura de árbol. Muy parecido a lo que sería un sistema de ficheros clásico pero siendo todos los nodos iguales, sin que haya distinción entre directorios y ficheros: todos los nodos son accesibles, pueden contener datos y pueden tener nodos hijos. Cada nodo puede contener cualquier tipo de información ya que el acceso es binario, pero el sistema está diseñado para que sean de un tamaño moderado, unos pocos kilobytes como mucho.
Los clientes tienen configuradas una o varias de las instancias del quorum, que no tienen por qué ser todas los integrantes, y que son los que se encargan de presentar al cliente al líder en ese momento.
Los usos más habituales de este servicio es como repositorio de información de configuración y nombres, o para proveer mecanismos de sincronización (lock, mutex,…) para soluciones distribuidas.
Un ejemplo de uso que me parece muy apropiado puede ser como repositorio de configuración de un conjunto de componentes muy numeroso, por ejemplo los sistemas de red de una gran organización o los diferentes servicios de una granja de servidores. Cada nodo hoja de la estructura de árbol puede ser un sistema o servicio y la información que contiene su configuración o estado. Los nodos rama nos permiten organizar, agrupar y clasificar los sistemas o servicios de la manera más adecuada para la organización. La configuración de todos los sistemas o servicios se gestiona de manera centralizada (solo hay una configuración aún estando replicada), la solución es muy escalable y tiene alta disponibilidad (levantando más instancias podemos servir a más clientes) y es también deslocalizable (al permitir desplegar instancias en diferentes lugares buscando la idoneidad específica para cada cliente).
Es también común verla como mecanismo de sincronización o como monitor de soluciones distribuidas como en Druid, por ejemplo.
Docker
Docker es una plataforma para la virtualización de entornos a nivel de aplicación o software base en lugar de sistema operativo o máquina virtual. Docker permite definir las diferentes unidades de despliegue de la solución software, con sus componentes distribuidos y sus límites de interconexión con otras unidades, para permitir su despliegue apilado de tal manera que representen las diferentes capas de las soluciones.
La idea es que, en lugar de crear una máquina virtual completa para cada entorno con sus recursos virtuales y sistema operativo, sea el propio sistema operativo el que aísle y compartimente la ejecución de cada entorno, denominado container. El propio sistema operativo crea un entorno de ejecución aislado a nivel de sistema de ficheros, de red, de seguridad y ejecución; de tal manera que los diferentes entornos, aún ejecutando en el mismo sistema operativo, están aislados entre ellos o solo compartiendo aquellas partes que se deseen.
Docker para implementar su funcionalidad se basa en dos características del kernel de Linux, cgroups y namespaces, que permiten establecer criterios de prioridad de CPU, limitación de memoria o de operaciones de I/O o control de facturación en el primer caso; o aislar procesos, entornos de red, usuarios, sistemas de ficheros entre los diferentes entornos en el segundo.
Cada entorno en Docker en ejecución es un container, que se corresponde con el despliegue de una imagen, que contiene la definición del entorno, sus límites, puntos de interconexión con terceros y componentes software. Los containers pueden apilarse de tal manera que cada uno represente una capa de la solución final. La combinación apilada de containers se produce mediante la definición de los puntos de interconexión de los container, como pueden ser puertos de red, y la sobreposición de los sistemas de ficheros uno sobre otro, mediante UnionFS.
La posibilidad de dividir los diferentes componentes distribuidos y capas del aplicativo en diferentes containers apilables supone una gran ventaja tanto para desarrolladores como para técnicos de explotación. Por un lado las dependencias se resuelven dentro de los propios container de la solución por lo que dentro de un mismo sistema no hay incompatibilidades entre versiones y dependencias. Por otro, el proceso de despligue se modulariza y la nivelación de entornos de integración y productivos es mucho más sencilla, al basarse en el despliegue secuencial de estas unidades.
Las imágenes que se despliegan como containers pueden registrarse en directorios públicos o privados (dentro del ámbito de la organización) con Docker Hub. El servicio en la nube que registra, almacena, distribuye y da soporte social y colaborativo a la tarea de gestión de las diferentes imágenes. Este es el servicio que monetiza toda la idea. Secundo la opinión de D’Oh de que puede ser una excelente alternativa o mecanismo en el que basarse las futuras tiendas de aplicaciones o sistemas de paquetes de los sistemas operativos.
Aunque la tecnología está basada en características propias de Linux, existen versiones portadas a Windows o iOS, pero con un componente adicional de virtualización para dar soporte a estas funcionalidades de compartimentación que no existen, al menos de manera idéntica, en estos sistemas. Las plataformas de virtualización en la nube Amazon EC2 y Google Cloud también soportan de manera nativa Docker.
gRPC y Proto 3
Google hace público el protocolo o mecanismo RPC que utiliza en alguno de sus sistemas, gRPC:
https://github.com/grpc/grpc-common
Para el envío de mensajes utiliza el protocolo Protocols Buffers en su versión 3:
https://github.com/google/protobuf
Google asegura que son ligeras, rápidas y muy eficientes en cuanto al consumo de recursos. La verdad es que los ejemplos aparentan que es muy sencillo integrarlo.
Ambas opciones son multilenguaje/multiframework soportando C++, Java, Python, Go y Ruby entre otros (incluyendo la posibilidad de invocarse entre plataformas distintas). Pero sobre todo me llama la atención la opción de Android, porque hace tiempo buscando soluciones para RPC en este entorno no encontré ni muchas opciones, ni muy buenas.