Pub/Sub vs Queues: ¿Cuál elegir para tu aplicación?
Las arquitecturas modernas frecuentemente requieren comunicación asíncrona entre componentes y dos patrones comunes para lograr esto son pub/sub y queues de mensajería; este artículo literalmente fue inspirado en una pregunta de twitter y en una explicación que vi en una imagen, pero faltaba la explicación, así es que aquí voy:
Pub/Sub
El patrón pub/sub (publicar/suscribir) involucra enviar mensajes a un tema o canal en lugar de directamente a un receptor. Los publicadores envían mensajes al tema sin conocer a los suscriptores. Luego, los suscriptores reciben los mensajes según su interés en temas específicos.
Características de pub/sub:
- Comunicación asíncrona uno a muchos - un mensaje puede ser recibido por múltiples suscriptores.
- Los suscriptores se suscriben a temas explícitamente, abstractando al publicador.
- Entrega casi en tiempo real tan pronto el mensaje se publica.
Ejemplo en Python con Redis:
import redis
r = redis.Redis(...)
# Publicador
r.publish('noticias', 'Nuevo artículo publicado')
# Suscriptor 1
sub = r.pubsub()
sub.subscribe('noticias')
sub.parse_response() # ['message', 'noticias', 'Nuevo artículo publicado']
# Suscriptor 2
sub2 = r.pubsub()
sub2.subscribe('noticias')
sub2.parse_response() # ['message', 'noticias', 'Nuevo artículo publicado']
Pub/sub es útil para notificaciones en tiempo real y comunicación uno a muchos.
Queues
Las queues son colas FIFO donde los productores insertan mensajes y los consumidores los procesan en orden.
Características:
- Comunicación asíncrona uno a uno - un mensaje se procesa una vez por un consumidor.
- Desacoplamiento entre productor y consumidor.
- Retraso entre producir y consumir debido a la cola.
- Permite poner en cola trabajo para procesar más tarde.
Ejemplo en Python con RabbitMQ:
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='tasks')
# Productor
channel.basic_publish(exchange='', routing_key='tasks', body='Enviar email al usuario')
# Consumidor
def callback(ch, method, properties, body):
print(" [x] Recibido %r" % body)
# procesar tarea
channel.basic_consume(queue='tasks', on_message_callback=callback, auto_ack=True)
Las queues son útiles para trabajos en background, nivelar cargas y desacoplar sistemas.
Comparativa
Pub/Sub | Queues |
---|---|
Uno a muchos | Uno a uno |
Tiempo real | Retraso |
Acoplado | Desacoplado |
Suscripción explícita | Abstracto para consumidores |
Redis, Kafka | RabbitMQ, Kafka |
Otras opciones
Existen múltiples plataformas para implementar pub/sub y queues, cada una con sus capacidades:
- Redis: simple, rápido, ampliamente utilizado.
- RabbitMQ: excelente soporte para queues, usado empresarialmente.
- Kafka: altamente escalable, muy rápido, buen soporte pub/sub y queues.
- AWS SNS y SQS: servicios de mensajería de AWS, fácil integración.
- Google Cloud Pub/Sub: servicio de GCP para messaging.
- Azure Service Bus: opción de Microsoft Azure.
La elección depende de tus necesidades específicas de cada proyecto, y como mencionado anteriormente, mi patrón de comunicación entre aplicaciones es normalmente Bullmq, Rabbitmq y finalmente Kafka, porque para serles sincero, muy pocas veces he utilizado pub/sub.
Y antes de finalizar este artículo, he de mencionar que RabbitMQ con los exchanges tienen una gran similitud al patrón pub/sub, aunque funcionan de forma diferente porque los exchanges reciben el mensaje y lo publican en diferentes queues, es una forma de emular pub/sub "diferente".
Happy coding! :D
Photo by Omar Flores on Unsplash