Saltar al contenido

Un nuevo concursante para destronar al Rey: Apache Pulsar

Apache Kafka parece ser la solución estándar en la arquitectura actual, pero deberíamos centrarnos en si es la elección correcta para nuestras necesidades.

Foto de Ross Sokolovski en Unsplash

Hoy en día, estamos en una nueva era de Arquitectura Orientada a Eventos, y esta no es la primera vez que vivimos eso. Antes de los microservicios y la nube, EDA era la nueva normalidad en la integración empresarial. Basado en diferentes tipos de estándares, había protocolos como JMS o AMQP utilizados en productos basados en brokers como TIBCO EMS, Active MQ o IBM Websphere MQ, por lo que este enfoque no es algo nuevo.

Con el auge de las arquitecturas de microservicios y el enfoque liderado por API, parecía que habíamos olvidado la importancia de los sistemas de mensajería, y tuvimos que pasar por los mismos desafíos que vimos en el pasado para llegar a una nueva solución de mensajería para resolver ese problema. Así que estamos volviendo a la Arquitectura EDA, mecanismo pub-sub, para ayudarnos a desacoplar los consumidores y productores, moviéndonos de la orquestación a la coreografía, y todos estos conceptos encajan mejor en los mundos actuales con componentes cada vez más independientes que necesitan cooperación e integración.

Durante este esfuerzo, comenzamos a mirar nuevas tecnologías para ayudarnos a implementar eso nuevamente. Sin embargo, con la nueva realidad, olvidamos los protocolos y estándares pesados como JMS y comenzamos a pensar en otras opciones. Y debemos admitir que sentimos que hay un nuevo rey en esta área, y este es uno de los componentes críticos que parece ser, sin importar qué, en la arquitectura de hoy: Apache Kafka.

Y no me malinterpretes. Apache Kafka es fantástico, y ha sido probado durante mucho tiempo, una solución lista para producción, de alto rendimiento, con capacidades impresionantes para la reproducción y una API poderosa para facilitar la integración. Apache Kafka tiene algunos desafíos en este mundo nativo de la nube porque no se lleva tan bien con algunas de sus reglas.

Si has usado Apache Kafka durante algún tiempo, eres consciente de que hay desafíos particulares con él. Apache Kafka tiene una arquitectura que proviene de sus días en LinkedIn en 2011, donde Kubernetes o incluso Docker y las tecnologías de contenedores no eran una cosa, lo que hace que ejecutar Apache Kafka (servicio puramente con estado) de manera contenedorizada sea bastante complicado. Hay mejoras usando gráficos de helm y operadores para facilitar el viaje, pero aún así, no se siente como si las piezas pudieran integrarse bien de esa manera. Otra cosa es la geo-replicación que incluso con componentes como MirrorMaker, no es algo que se use, funcione sin problemas y se sienta integrado.

Otras tecnologías están tratando de proporcionar una solución para esas capacidades, y una de ellas es también otro proyecto de la Fundación Apache que ha sido donado por Yahoo! y se llama Apache Pulsar.

No me malinterpretes; esto no se trata de encontrar una nueva verdad, esa única solución de mensajería que es perfecta para las arquitecturas de hoy: no existe. En el mundo actual, con tantos requisitos y variables diferentes para los diferentes tipos de aplicaciones, una talla única ya no es cierta. Así que deberías dejar de pensar en cuál solución de mensajería es la mejor, y pensar más en cuál sirve mejor a tu arquitectura y cumple con los requisitos tanto técnicos como comerciales.

Hemos cubierto diferentes formas de comunicación general, con varias soluciones específicas para la comunicación sincrónica (tecnologías de malla de servicios y protocolos como REST, GraphQL o gRPC) y diferentes para la comunicación asincrónica. Necesitamos profundizar en la comunicación asincrónica para encontrar lo que funciona mejor para ti. Pero primero, hablemos un poco más sobre Apache Pulsar.

Apache Pulsar

Apache Pulsar, como se mencionó anteriormente, ha sido desarrollado internamente por Yahoo! y donado a la Fundación Apache. Como se indica en su sitio web oficial, hay varios puntos clave a mencionar al comenzar a explorar esta opción:

  • Funciones de Pulsar: Despliega fácilmente lógica de cómputo ligera usando APIs amigables para desarrolladores sin necesidad de ejecutar tu motor de procesamiento de flujos
  • Probado en producción: Apache Pulsar ha funcionado en producción a escala Yahoo durante más de tres años, con millones de mensajes por segundo a través de millones de temas
  • Escalabilidad horizontal: Expande la capacidad sin problemas a cientos de nodos
  • Baja latencia con durabilidad: Diseñado para baja latencia de publicación (< 5ms) a escala con fuertes garantías de durabilidad
  • Geo-replicación: Diseñado para replicación configurable entre centros de datos en múltiples regiones geográficas
  • Multi-tenencia: Construido desde cero como un sistema multi-tenant. Soporta Aislamiento, Autenticación, Autorización y Cuotas
  • Almacenamiento persistente: Almacenamiento persistente de mensajes basado en Apache BookKeeper. Proporciona aislamiento a nivel de IO entre operaciones de escritura y lectura
  • Bibliotecas de clientes: Modelos de mensajería flexibles con APIs de alto nivel para Java, C++, Python y GO
  • Operabilidad: API de administración REST para aprovisionamiento, administración, herramientas y monitoreo. Despliega en metal desnudo o Kubernetes.

Como podemos ver, en su diseño, Apache Pulsar está abordando algunas de las principales debilidades de Apache Kafka como la Geo-replicación y su enfoque nativo de la nube.

Apache Pulsar proporciona soporte para el patrón pub/sub, pero también ofrece muchas capacidades que también lo colocan como un sistema de mensajería de cola tradicional con su concepto de temas exclusivos donde solo uno de los suscriptores recibirá el mensaje. También proporciona conceptos y características interesantes utilizados en otros sistemas de mensajería:

  • Temas de Carta Muerta: Para mensajes que no pudieron ser procesados por el consumidor.
  • Temas Persistentes y No Persistentes: Para decidir si deseas persistir tus mensajes o no durante la transición.
  • Espacios de Nombres: Para tener una distribución lógica de tus temas, de modo que una aplicación pueda agruparse en espacios de nombres como lo hacemos, por ejemplo, en Kubernetes para poder aislar algunas aplicaciones de las otras.
  • Failover: Similar a exclusivo, pero cuando el consumidor adjunto falla al procesar, otro toma la oportunidad de procesar los mensajes.
  • Compartido: Para poder proporcionar un enfoque de round-robin similar al sistema de mensajería de cola tradicional donde todos los suscriptores estarán adjuntos al tema, pero solo uno recibirá el mensaje, y distribuirá la carga entre todos ellos.
  • Suscripciones Multi-tema: Para poder suscribirse a varios temas usando una expresión regular (similar al enfoque de Subject de TIBCO Rendezvous, por ejemplo, en los años 90) que ha sido tan poderoso y popular.

Pero también, si requieres características de Apache Kafka, aún tendrás conceptos similares como temas particionados, temas compartidos por clave, y así sucesivamente. Así que tienes todo a tu disposición para elegir qué tipo de configuración funciona mejor para ti y tus casos de uso específicos, también tienes la opción de mezclar y combinar.

Arquitectura de Apache Pulsar

La Arquitectura de Apache Pulsar es similar a otros sistemas de mensajería comparables hoy en día. Como puedes ver en la imagen a continuación del sitio web de Apache Pulsar, estos son los componentes principales de la arquitectura:

  • Brokers: Uno o más brokers manejan mensajes entrantes de productores, despachan mensajes a consumidores
  • Cluster de BookKeeper para almacenamiento persistente de gestión de mensajes
  • Cluster de ZooKeeper para propósitos de gestión.

Así que puedes ver que esta arquitectura también es bastante similar a la de Apache Kafka nuevamente con la adición de un nuevo concepto del Cluster de BookKeeper.

El Broker en Apache Pulsar son componentes sin estado que principalmente ejecutarán dos piezas

  • Servidor HTTP que expone una API REST para gestión y es utilizado por consumidores y productores para búsqueda de temas.
  • Servidor TCP usando un protocolo binario llamado dispatcher que se utiliza para todas las transferencias de datos. Por lo general, los mensajes se despachan desde un caché de libro mayor gestionado por razones de rendimiento. Pero también si este caché crece demasiado, interactuará con el cluster de BookKeeper por razones de persistencia.

Para soportar la Replicación Global (Geo-Replicación), los Brokers gestionan replicadores que siguen las entradas publicadas en la región local y las republican en las regiones remotas.

El Cluster de Apache BookKeeper se utiliza como almacenamiento persistente de mensajes. Apache BookKeeper es un sistema de registro de escritura anticipada (WAL) distribuido que gestiona cuándo los mensajes deben ser persistidos. También soporta escalado horizontal basado en la carga y soporte multi-log. No solo los mensajes son persistentes sino también los cursores que son la posición del consumidor para un tema específico (similar al offset en la terminología de Apache Kafka)

Finalmente, el Cluster de Zookeeper se utiliza en el mismo rol que Apache Kafka como un cluster de almacenamiento de configuración de metadatos para todo el sistema.

Hola Mundo usando Apache Pulsar

Veamos cómo podemos crear un caso rápido de «Hola Mundo» usando Apache Pulsar como protocolo, y para hacer eso, vamos a intentar implementarlo de manera nativa en la nube. Así que haremos un cluster de un solo nodo de Apache Pulsar en una instalación de Kubernetes y desplegaremos una aplicación productora usando la tecnología Flogo y una aplicación consumidora usando Go. Algo similar a lo que puedes ver en el diagrama a continuación:

Diagrama sobre el caso de prueba que estamos haciendo

Y vamos a intentar mantenerlo simple, así que solo usaremos docker puro esta vez. Así que, primero que nada, solo inicia el servidor de Apache Pulsar y para hacer eso usaremos el siguiente comando:

docker run -it -p 6650:6650 -p 8080:8080 --mount source=pulsardata,target=/pulsar/data --mount source=pulsarconf,target=/pulsar/conf apachepulsar/pulsar:2.5.1   bin/pulsar standalone

Y veremos una salida similar a esta:

Ahora, necesitamos crear aplicaciones simples, y para eso, se utilizarán Flogo y Go.

Comencemos con el productor, y en este caso, usaremos la versión de código abierto para crear una aplicación rápida.

Primero que nada, solo usaremos la interfaz web (dockerizada) para hacer eso. Ejecuta el comando:

docker run -it -p 3303:3303 flogo/flogo-docker eula-accept

E instalamos una nueva contribución para habilitar la actividad del publicador de Pulsar. Para hacer eso, haremos clic en el botón «Instalar nueva contribución» y proporcionaremos la siguiente URL:

flogo install github.com/mmussett/flogo-components/activity/pulsar

Y ahora crearemos un flujo simple como puedes ver en la imagen a continuación:

Ahora construiremos la aplicación usando el menú, ¡y eso es todo!

Para poder ejecutar, solo lanza la aplicación como puedes ver aquí:

./sample-app_linux_amd64

Ahora, solo necesitamos crear el consumidor en Go-lang para poder hacer eso necesitamos instalar el paquete golang:

go get github.com/apache/pulsar-client-go/pulsar

Y ahora necesitamos crear el siguiente código:

package main
import (
 “fmt”
 “log”
 “github.com/apache/pulsar-client-go/pulsar”
)
func main() {
 client, err := pulsar.NewClient(pulsar.ClientOptions{URL: “pulsar://localhost:6650”})
 if err != nil {
 log.Fatal(err)
 }
defer client.Close()
channel := make(chan pulsar.ConsumerMessage, 100)
options := pulsar.ConsumerOptions{
 Topic: “counter”,
 SubscriptionName: “my-subscription”,
 Type: pulsar.Shared,
 }
options.MessageChannel = channel
consumer, err := client.Subscribe(options)
 if err != nil {
 log.Fatal(err)
 }
defer consumer.Close()
// Recibe mensajes del canal. El canal devuelve una estructura que contiene el mensaje y el consumidor de donde
 // se recibió el mensaje. No es necesario aquí ya que tenemos un solo consumidor, pero el canal podría ser
 // compartido entre múltiples consumidores también
 for cm := range channel {
 msg := cm.Message
 fmt.Printf(“Mensaje recibido msgId: %v — contenido: ‘%s’n”,
 msg.ID(), string(msg.Payload()))
consumer.Ack(msg)
}
}

Y después de ejecutar ambos programas, puedes ver la siguiente salida como puedes ver, pudimos comunicar ambas aplicaciones en un flujo sin esfuerzo.

Este artículo es solo un punto de partida, y continuaremos hablando sobre cómo usar Apache Pulsar en tus arquitecturas. Si deseas echar un vistazo al código que hemos utilizado en este ejemplo, puedes encontrarlo aquí:

One new contestant to bring down the King: Apache Pulsar

Apache Kafka seems to be the standard solution in nowadays architecture, but we should focus if it is the right choice for our needs.

Photo by Ross Sokolovski on Unsplash

Nowadays, we’re in a new age of Event-Driven Architecture, and this is not the first time we’ve lived that. Before microservices and cloud, EDA was the new normal in enterprise integration. Based on different kinds of standards, there where protocols like JMS or AMQP used in broker-based products like TIBCO EMS, Active MQ, or IBM Websphere MQ, so this approach is not something new.

With the rise of microservices architectures and the API lead approach, it seemed that we’ve forgotten about the importance of the messaging systems, and we had to go through the same challenges we saw in the past to come to a new messaging solution to solve that problem. So, we’re coming back to EDA Architecture, pub-sub mechanism, to help us decouple the consumers and producers, moving from orchestration to choreography, and all these concepts fit better in nowaday worlds with more and more independent components that need cooperation and integration.

During this effort, we started to look at new technologies to help us implement that again. Still, with the new reality, we forgot about the heavy protocols and standards like JMS and started to think about other options. And we need to admit that we felt that there is a new king in this area, and this is one of the critical components that seem to be no matter what in today’s architecture: Apache Kafka.

And don’t get me wrong. Apache Kafka is fantastic, and it has been proven for so long, a production-ready solution, performant, with impressive capabilities for replay and powerful API to ease the integration. Apache Kafka has some challenges in this cloud-native world because it doesn’t play so well with some of its rules.

If you have used Apache Kafka for some time, you are aware that there are particular challenges with it. Apache Kafka has an architecture that comes from its LinkedIn days in 2011, where Kubernetes or even Docker and container technologies were not a thing, that makes to run Apache Kafka (purely stateful service) in a container fashion quite complicated. There are improvements using helm charts and operators to ease the journey, but still, it doesn’t feel like pieces can integrate well into that fashion. Another thing is the geo-replication that even with components like MirrorMaker, it is not something used, works smooth, and feels integrated.

Other technologies are trying to provide a solution for those capabilities, and one of them is also another Apache Foundation project that has been donated by Yahoo! and it is named Apache Pulsar.

Don’t get me wrong; this is not about finding a new truth, that single messaging solution that is perfect for today’s architectures: it doesn’t exist. In today’s world, with so many different requirements and variables for the different kinds of applications, one size fits all is no longer true. So you should stop thinking about which messaging solution is the best one, and think more about which one serves your architecture best and fulfills both technical and business requirements.

We have covered different ways for general communication, with several specific solutions for synchronous communication (service mesh technologies and protocols like REST, GraphQL, or gRPC) and different ones for asynchronous communication. We need to go deeper into the asynchronous communication to find what works best for you. But first, let’s speak a little bit more about Apache Pulsar.

Apache Pulsar

Apache Pulsar, as mentioned above, has been developed internally by Yahoo! and donated to the Apache Foundation. As stated on their official website, they are several key points to mention as we start exploring this option:

  • Pulsar Functions: Easily deploy lightweight compute logic using developer-friendly APIs without needing to run your stream processing engine
  • Proven in production: Apache Pulsar has run in production at Yahoo scale for over three years, with millions of messages per second across millions of topics
  • Horizontally scalable: Seamlessly expand capacity to hundreds of nodes
  • Low latency with durability: Designed for low publish latency (< 5ms) at scale with strong durability guarantees
  • Geo-replication: Designed for configurable replication between data centers across multiple geographic regions
  • Multi-tenancy: Built from the ground up as a multi-tenant system. Supports Isolation, Authentication, Authorization, and Quotas
  • Persistent storages: Persistent message storage based on Apache BookKeeper. Provides IO-level isolation between write and read operations
  • Client libraries: Flexible messaging models with high-level APIs for Java, C++, Python and GO
  • Operability: REST Admin API for provisioning, administration, tools, and monitoring. Deploy on bare metal or Kubernetes.

So, as we can see, in its design, Apache Pulsar is addressing some of the main weaknesses of Apache Kafka as Geo-replication and their cloud-native approach.

Apache Pulsar provides support for the pub/sub pattern, but also provides so many capabilities that also place as a traditional queue messaging system with their concept of exclusive topics where only one of the subscribers will receive the message. Also provides interesting concepts and features used in other messaging systems:

  • Dead Letter Topics: For messages that were not able to be processed by the consumer.
  • Persistent and Non-Persistent Topics: To decide if you want to persist your messages or not during the transition.
  • Namespaces: To have a logical distribution of your topics, so an application can be grouped in namespaces as we do, for example, in Kubernetes so we can isolate some applications from the others.
  • Failover: Similar to exclusive, but when the attached consumer failed to process another takes the chance to process the messages.
  • Shared: To be able to provide a round-robin approach similar to the traditional queue messaging system where all the subscribers will be attached to the topic, but the only one will receive the message, and it will distribute the load along all of them.
  • Multi-topic subscriptions: To be able to subscribe to several topics using a regexp (similar to the Subject approach from TIBCO Rendezvous, for example, in the 90s) that has been so powerful and popular.

But also, if you require features from Apache Kafka, you will still have similar concepts as partitioned topics, key-shared topics, and so on. So you have everything at your hand to choose which kind of configuration works best for you and your specific use cases, you also have the option to mix and match.

Apache Pulsar Architecture

Apache Pulsar Architecture is similar to other comparable messaging systems today. As you can see in the picture below from the Apache Pulsar website, those are the main components of the architecture:

  • Brokers: One or more brokers handles incoming messages from producers, dispatches messages to consumers
  • BookKeeper Cluster for persistent storage of messages management
  • ZooKeeper Cluster for management purposes.

So you can see this architecture is also quite similar to the Apache Kafka one again with the addition of a new concept of the BookKeeper Cluster.

Broker in Apache Pulsar are stateless components that mainly will run two pieces

  • HTTP Server that exposes a REST API for management and is used by consumers and producers for topic lookup.
  • TCP Server using a binary protocol called dispatcher that is used for all the data transfers. Usually, Messages are dispatched out of a managed ledger cache for performance purposes. But also if this cache grows too big, it will interact with the BookKeeper cluster for persistence reasons.

To support the Global Replication (Geo-Replication), the Brokers manage replicators that tail the entries published in the local region and republish them to the remote regions.

Apache BookKeeper Cluster is used as persistent message storage. Apache BookKeeper is a distributed write-ahead log (WAL) system that manages when messages should be persisted. It also supports horizontal scaling based on the load and multi-log support. Not only messages are persistent but also the cursors that are the consumer position for a specific topic (similar to the offset in Apache Kafka terminology)

Finally, Zookeeper Cluster is used in the same role as Apache Kafka as a metadata configuration storage cluster for the whole system.

Hello World using Apache Pulsar

Let’s see how we can create a quick “Hello World” case using Apache Pulsar as a protocol, and to do that, we’re going to try to implement it in a cloud-native fashion. So we will do a single-node cluster of Apache Pulsar in a Kubernetes installation and deploy a producer application using Flogo technology and a consumer application using Go. Something similar to what you can see in the diagram below:

Diagram about the test case we’re doing

And we’re going to try to keep it simple, so we will just use pure docker this time. So, first of all, just spin up the Apache Pulsar server and to do that we will use the following command:

docker run -it -p 6650:6650 -p 8080:8080 --mount source=pulsardata,target=/pulsar/data --mount source=pulsarconf,target=/pulsar/conf apachepulsar/pulsar:2.5.1   bin/pulsar standalone

And we will see an output similar to this one:

Now, we need to create simple applications, and for that, Flogo and Go will be used.

Let’s start with the producer, and in this case, we will use the open-source version to create a quick application.

First of all, we will just use the Web UI (dockerized) to do that. Run the command:

docker run -it -p 3303:3303 flogo/flogo-docker eula-accept

And we install a new contribution to enable the Pulsar publisher activity. To do that we will click on the “Install new contribution” button and provide the following URL:

flogo install github.com/mmussett/flogo-components/activity/pulsar

And now we will create a simple flow as you can see in the picture below:

We will now build the application using the menu, and that’s it!

To be able to run just launch the application as you can see here:

./sample-app_linux_amd64

Now, we just need to create the Go-lang consumer to be able to do that we need to install the golang package:

go get github.com/apache/pulsar-client-go/pulsar

And now we need to create the following code:

package main
import (
 “fmt”
 “log”
 “github.com/apache/pulsar-client-go/pulsar”
)
func main() {
 client, err := pulsar.NewClient(pulsar.ClientOptions{URL: “pulsar://localhost:6650”})
 if err != nil {
 log.Fatal(err)
 }
defer client.Close()
channel := make(chan pulsar.ConsumerMessage, 100)
options := pulsar.ConsumerOptions{
 Topic: “counter”,
 SubscriptionName: “my-subscription”,
 Type: pulsar.Shared,
 }
options.MessageChannel = channel
consumer, err := client.Subscribe(options)
 if err != nil {
 log.Fatal(err)
 }
defer consumer.Close()
// Receive messages from channel. The channel returns a struct which contains message and the consumer from where
 // the message was received. It’s not necessary here since we have 1 single consumer, but the channel could be
 // shared across multiple consumers as well
 for cm := range channel {
 msg := cm.Message
 fmt.Printf(“Received message msgId: %v — content: ‘%s’\n”,
 msg.ID(), string(msg.Payload()))
consumer.Ack(msg)
}
}

And after running both programs, you can see the following output as you can see, we were able to communicate both applications in an effortless flow.

This article is just a starting point, and we will continue talking about how to use Apache Pulsar in your architectures. If you want to take a look at the code we’ve used in this sample, you can find it here: