¿Cómo implementar una cola de mensajes sobre Redis?

29

¿Por qué Redis para hacer cola?

Tengo la impresión de que Redis puede ser un buen candidato para implementar un sistema de colas. Hasta este momento, hemos estado utilizando nuestra base de datos MySQL con sondeo, o RabbitMQ. Con RabbitMQ hemos tenido muchos problemas: las bibliotecas del cliente son muy pobres y tienen errores y nos gustaría no invertir demasiadas horas de desarrollador en solucionarlos, algunos problemas con la consola de administración del servidor, etc. Y, por el momento siendo al menos, no estamos tratando de alcanzar milisegundos o presionando seriamente el rendimiento, por lo que mientras un sistema tenga una arquitectura que soporte una cola de manera inteligente, probablemente estemos en buena forma.

Bien, ese es el trasfondo. Esencialmente tengo un modelo de cola muy clásico y simple: varios productores que producen trabajo y varios consumidores que consumen trabajo, y tanto los productores como los consumidores deben poder escalar de manera inteligente. Resulta que un ingenuo PUBSUBno funciona, ya que no quiero que todos los suscriptores consuman trabajo, solo quiero que un suscriptor reciba el trabajo. A primera vista, me parece que BRPOPLPUSHes un diseño inteligente.

¿Podemos usar BRPOPLPUSH?

El diseño básico BRPOPLPUSHes que tiene una cola de trabajo y una cola de progreso. Cuando un consumidor recibe trabajo, empuja atómicamente el elemento a la cola de progreso, y cuando completa el trabajo, LREMes él. Esto evita el bloqueo del trabajo si los clientes mueren y hace que el monitoreo sea bastante fácil; por ejemplo, podemos saber si hay un problema que hace que los consumidores tomen mucho tiempo para realizar tareas, además de saber si hay un gran volumen de tareas.

Se asegura

  • el trabajo se entrega exactamente a un consumidor
  • el trabajo termina en una cola de progreso, por lo que no puede bloquearse si un consumidor

Los inconvenientes

  • Me parece bastante extraño que el mejor diseño que he encontrado en realidad no lo use, PUBSUBya que esto parece ser en lo que se enfoca la mayoría de las publicaciones de blog sobre hacer cola en Redis. Entonces siento que me falta algo obvio. La única forma que veo de usar PUBSUBsin consumir tareas dos veces es simplemente enviar una notificación de que el trabajo ha llegado, que los consumidores pueden sin bloquear RPOPLPUSH.
  • Es imposible solicitar más de un elemento de trabajo a la vez, lo que parece ser un problema de rendimiento. No es muy importante para nuestra situación, pero obviamente dice que esta operación no fue diseñada para un alto rendimiento o esta situación
  • En resumen: ¿me estoy perdiendo algo estúpido?

También agrego la etiqueta node.js, porque ese es el lenguaje con el que estoy tratando principalmente. Node puede ofrecer algunas simplificaciones en la implementación, dada su naturaleza de subproceso único y no bloqueante, pero además estoy usando la biblioteca de nodo-redis y las soluciones deberían o pueden ser sensibles a sus fortalezas y debilidades también.

djechlin
fuente

Respuestas:

5

Si desea usar Redis para una cola de mensajes en Node.js y no le importa usar un módulo para eso, puede probar RSMQ , la Cola de mensajes simples de Redis para Node. No estaba disponible cuando se hizo esta pregunta, pero hoy es una opción viable.

Si realmente desea implementar la cola usted mismo como lo indicó en su pregunta, puede leer la fuente de RSMQ porque son solo 20 pantallas de código que hacen exactamente lo que está pidiendo.

Ver:

rsp
fuente
Aceptaré esto a menos que luego sepa que es realmente defectuoso o roto o algo así.
djechlin
22

Me he topado con algunas dificultades hasta ahora que me gustaría documentar aquí.

¿Cómo manejas la lógica de reconexión?

Este es un problema difícil y un problema especialmente difícil en el diseño e implementación de una cola de mensajes. Los mensajes deben poder hacer cola en algún lugar cuando los consumidores están desconectados, por lo que un simple pub-sub no es lo suficientemente fuerte y los consumidores deben volver a conectarse en un estado de escucha. El bloqueo de pops es un estado difícil de mantener, porque son un estado de escucha no idempotente . Escuchar debe ser una operación idempotente, pero cuando se trata de una desconexión con respecto a un pop de bloqueo, tiene el placer de pensar mucho sobre si la desconexión ocurrió justo después de que la operación tuvo éxito o justo antes de que la operación fallara. Esto no es insuperable, pero es indeseable.

Además, la operación de escucha debe ser lo más simple posible. Idealmente debería tener estas propiedades:

  • Escuchar es idempotente.
  • El consumidor siempre está escuchando, y la lógica de aceleración se procesa fuera del código lógico de escucha. RabbitMQ encapsula esto al permitir que el consumidor limite el número de mensajes no desbloqueados que puede tener.
    En particular, elegí un diseño deficiente en el que volver a ingresar a un pop de bloqueo dependía del éxito de las operaciones anteriores, que era frágil y requería pensar mucho.

Ahora estoy a favor de una solución Redis PUBSUB + RPOPLPUSH. Esto desacopla la notificación de trabajo del consumo de trabajo, lo que nos permite descifrar una solución de escucha limpia. El PUBSUB solo es responsable de la notificación del trabajo. La naturaleza atómica de RPOPLPUSH es responsable del consumo y de delegar el trabajo exactamente a un consumidor. Al principio, esta solución parecía innecesariamente complicada en comparación con un pop de bloqueo, pero ahora veo que la complicación no era innecesaria en absoluto; Estaba resolviendo un problema difícil.

Sin embargo, esta solución no es del todo trivial:

  • los consumidores también deben verificar el trabajo de reconexión.
  • los consumidores pueden querer hacer una encuesta para nuevos trabajos de todos modos, para redundancia. Si la encuesta realmente tiene éxito, se debe emitir una advertencia, ya que esto solo debe ocurrir entre el consumo en el PUBSUB y la encuesta en un RPOPLPUSH. Por lo tanto, muchos éxitos de encuestas indican un sistema de suscripción roto.

Tenga en cuenta que el diseño PUBSUB / RPOPLPUSH también tiene problemas de escala. Cada consumidor recibe una notificación ligera de cada mensaje, lo que significa que tiene un cuello de botella innecesario. Sospecho que es posible usar canales para fragmentar el trabajo, pero este es probablemente un diseño complicado para que funcione bien.

djechlin
fuente
No estoy seguro de seguir el problema de bloquear a los consumidores. Me parece que si no hay trabajo para procesar, los consumidores deberían bloquear hasta que haya algo, aunque supongo que si el consumidor también está haciendo otras cosas que podrían ser una historia diferente, pero no es más un problema dentro de la aplicación y no tanto por la cola? IE no bloquearía un hilo dentro de una aplicación más grande sería una solución más elegante, donde el hilo podría notificar a la aplicación cuando había recuperado un trabajo de la cola. Quizás es solo el uso del nodo lo que está creando la complicación.
AaronM
9
Tengo curiosidad por saber hasta dónde has llegado desde agosto pasado. ¿Pudiste resolver tus problemas a tu entera satisfacción? Como los resolviste?
AaronM
3
AAA: al igual que @AaronM, me encantaría saber cómo has progresado.
bjornl
Convenido. ¿Cómo ha progresado esto? Me gusta la idea de eliminar RabbitMQ de la pila y usar Redis que está allí de todos modos. Mi problema es cómo registrar un consumidor utilizando RSMQ (nodo lib).
ra9r
@raiglstorfer no ha trabajado allí durante dos años: P no dude en investigar y publicar ...
djechlin
0

Entonces, la razón más importante para elegir usar RabbitMQ sobre Redis son los escenarios de falla y la agrupación.

Este artículo realmente lo explica mejor, así que solo proporcionaré el enlace:

https://aphyr.com/posts/283-jepsen-redis

Redis Sentinel y más recientemente Redis Clustering no pueden manejar una serie de escenarios de falla muy básicos que lo convirtieron en una mala elección para una cola.

RabbitMQ tiene su propio conjunto de problemas, sin embargo, dicho esto, es increíblemente sólido en producción y es una buena cola de mensajes.

Aquí está la publicación para conejo:

https://aphyr.com/posts/315-jepsen-rabbitmq

Cuando observa el teorio CAP (consistencia, disponibilidad y manejo de particiones) solo puede elegir 2 de 3. Estamos aprovechando RMQ para el CP (consistencia y manejo de particiones) con nuestra carga de mensajes, si no estamos disponibles, no está t el fin del mundo. Para no perder mensajes, usamos ignorar para el manejo de la partición para no perder mensajes. Los duplicados se pueden manejar ya que la fuente administra el UUID.

ra9r
fuente