Solución de Problemas Comunes de Configuración de Redis Pub/Sub

Asegure una mensajería en tiempo real confiable dominando los desafíos de configuración de Redis Pub/Sub. Esta guía proporciona pasos prácticos para solucionar consumidores lentos, la causa principal de inestabilidad, utilizando la directiva crucial `client-output-buffer-limit`. Aprenda a diagnosticar picos de memoria con el comando `CLIENT LIST`, gestionar conexiones de suscriptores dedicadas e implementar mejores prácticas para el aislamiento de Pub/Sub de alto volumen para mantener la integridad del sistema.

Solución de Problemas Comunes de Configuración de Redis Pub/Sub.

Redis Pub/Sub es tan simple que los equipos a menudo lo tratan como un pequeño broker de mensajes que no necesitan operar. Un publicador llama a PUBLISH, los suscriptores reciben mensajes, y todo se siente instantáneo.

El problema comienza cuando intervienen redes reales, clientes lentos, reconexiones e instancias de Redis compartidas. Pub/Sub no tiene historial de mensajes, ni acuses de recibo, ni cola de reintentos. Es un mecanismo de transmisión en vivo. Si un suscriptor no está, el mensaje se pierde para ese suscriptor. Si un suscriptor está conectado pero no puede leer lo suficientemente rápido, Redis tiene que mantener la salida pendiente para ese cliente hasta que un límite configurado lo desconecte.

Eso hace que Redis Pub/Sub sea excelente para invalidación de caché, actualizaciones de presencia, paneles en vivo y notificaciones de "último valor". Es una mala opción para flujos de trabajo donde cada mensaje debe procesarse exactamente una vez o reproducirse después de una interrupción. Para eso, mire Redis Streams, RabbitMQ, Kafka u otro broker con seguimiento de entrega.

Síntoma: la memoria crece mientras el tráfico de Pub/Sub es alto

La falla más común de Pub/Sub es un suscriptor lento. El publicador está bien. Redis está bien al principio. Un consumidor está retrasado porque su proceso está en pausa, su red es lenta o realiza trabajo costoso dentro del manejador de mensajes. Redis sigue escribiendo mensajes en el búfer de salida de ese cliente. Si se permite que el búfer crezca sin un límite útil, la presión de la memoria se extiende de un mal suscriptor a toda la instancia.

Verifique los clientes:

redis-cli CLIENT LIST

Busque clientes con comandos Pub/Sub y gran memoria de salida:

id=88 addr=10.0.4.12:51244 flags=P db=0 sub=3 psub=0 omem=13421772 obl=128 oll=64 cmd=subscribe

Campos útiles:

  • flags=P indica un cliente Pub/Sub.
  • sub y psub muestran recuentos de suscripciones a canales y patrones.
  • omem es la memoria del búfer de salida en bytes.
  • obl y oll muestran detalles del backlog del búfer de salida.
  • cmd=subscribe o cmd=psubscribe confirma lo que está haciendo el cliente.

No se preocupe por idle para los suscriptores. Una conexión Pub/Sub puede permanecer inactiva desde el punto de vista del comando mientras aún recibe mensajes enviados.

Solucione consumidores lentos con límites de búfer de salida

Redis da a los clientes Pub/Sub su propia clase de límite de búfer de salida:

client-output-buffer-limit pubsub 32mb 8mb 60

Lea eso como:

  • Desconectar inmediatamente si el búfer de salida de un cliente Pub/Sub alcanza 32 MB.
  • Desconectar si permanece por encima de 8 MB durante 60 segundos.

Los números exactos deben coincidir con su tráfico y comportamiento del cliente. El error común es establecer el límite en 0 0 0 porque las desconexiones son molestas durante las pruebas. Eso elimina la barra de seguridad. En producción, una desconexión suele ser mejor que dejar que un suscriptor atascado consuma memoria hasta que Redis se vuelva inestable.

Después de cambiar redis.conf, recargue o reinicie según su proceso de implementación. También puede inspeccionar el valor en vivo:

redis-cli CONFIG GET client-output-buffer-limit

Si usa un servicio Redis administrado, esta configuración puede estar expuesta a través del grupo de parámetros o la interfaz de configuración de su proveedor, en lugar de la edición directa de archivos.

Síntoma: los suscriptores se conectan pero no reciben nada

Comience con la prueba más simple posible. Abra un terminal:

redis-cli SUBSCRIBE test.channel

Abra otro:

redis-cli PUBLISH test.channel hello

Si eso funciona desde el host de Redis pero no desde su aplicación, probablemente tenga un problema del lado del cliente o de red. Si falla en todas partes, verifique la autenticación, las ACL, el enrutamiento del clúster y el nombre real del canal.

Los nombres de los canales son cadenas de bytes exactas. events.user, events:User y events:user son canales diferentes. En sistemas más grandes, prefiero constantes o un pequeño módulo de nombres de canales en lugar de cadenas escritas a mano dispersas entre servicios.

Las suscripciones de patrones añaden otra fuente de confusión:

PSUBSCRIBE events:*

Esto coincide con events:user.created, pero no con prod:events:user.created. Si su aplicación prefija nombres de entorno, incluya el prefijo en el patrón.

Problemas de autenticación y ACL

Las implementaciones modernas de Redis a menudo usan usuarios ACL en lugar de una contraseña requirepass compartida. Un cliente Pub/Sub necesita permiso para conectarse, autenticarse, suscribirse al patrón de canal y, a veces, publicar.

Una verificación rápida con CLI:

redis-cli -u redis://app-user:[email protected]:6379 PING
redis-cli -u redis://app-user:[email protected]:6379 SUBSCRIBE events:test

Síntomas comunes de autenticación:

  • NOAUTH Authentication required: el cliente envió un comando antes de autenticarse.
  • WRONGPASS invalid username-password pair: contraseña incorrecta, usuario incorrecto o un secreto rotado no implementado en todas partes.
  • NOPERM this user has no permissions: el usuario se autenticó pero carece de permisos de comando o canal.

Con ACL, los permisos de canal están separados de los permisos de clave. Se puede permitir que un usuario ejecute comandos de clave normales pero no suscribirse a los canales que espera. Revise el usuario configurado:

redis-cli ACL GETUSER app-user

No pegue secretos de producción en el historial del shell mientras prueba. Use variables de entorno o una credencial temporal cuando sea posible.

No comparta conexiones Pub/Sub con comandos normales

Una vez que una conexión entra en modo suscrito, ya no es una conexión de comando Redis normal. Recibe mensajes enviados y solo puede emitir comandos relacionados con la suscripción como SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PING, RESET o comandos de estilo de salida dependiendo del cliente y la versión de Redis.

Si toma prestada una conexión de un grupo general y llama a SUBSCRIBE, esa conexión agrupada ya no está disponible para GET, SET u otros comandos ordinarios. El resultado puede parecer tiempos de espera aleatorios de la aplicación porque el grupo se llena lentamente con conexiones atascadas en modo suscriptor.

Use conexiones separadas:

  • Un cliente o grupo normal para comandos Redis regulares.
  • Una conexión de suscriptor de larga duración por proceso o hilo de suscriptor.
  • Una conexión de publicador separada si su biblioteca cliente lo recomienda.

Muchas bibliotecas cliente proporcionan un método duplicate() o new connection por esta razón. Úselo. No adivine que el grupo lo resolverá.

El comportamiento de reconexión importa

Pub/Sub no reproduce mensajes perdidos después de la reconexión. Si un suscriptor se cae durante 30 segundos, cada mensaje publicado durante esa ventana se pierde para ese suscriptor.

Eso es aceptable para la invalidación de caché solo si su aplicación tiene un respaldo. Por ejemplo, un servicio que pierde un evento de invalidación aún debe tener TTL, comprobaciones de versión o una forma de reconstruir desde la base de datos de origen. Sin un respaldo, un solo parpadeo de red puede dejar entradas de caché local obsoletas durante demasiado tiempo.

Al reconectarse, asegúrese de que el cliente se vuelva a suscribir a todos los canales y patrones. Algunas bibliotecas hacen esto automáticamente. Otras requieren que vuelva a registrar los manejadores después de la reconexión. Pruebe esto matando la conexión del suscriptor:

redis-cli CLIENT LIST | grep subscribe
redis-cli CLIENT KILL ID <client-id>

Luego publique un mensaje de prueba y confirme que la aplicación lo recibe después de reconectarse.

El alto fan-out puede convertirse en presión de CPU y red

El trabajo de PUBLISH crece con el número de suscriptores y patrones coincidentes. Un canal con miles de suscriptores significa que Redis tiene que enviar el mensaje miles de veces. Eso puede estar bien, pero no es gratis.

Vigile:

redis-cli INFO stats
redis-cli PUBSUB NUMSUB events:test
redis-cli PUBSUB NUMPAT

PUBSUB NUMSUB ayuda a confirmar si un canal tiene el número de suscriptores que cree que tiene. PUBSUB NUMPAT es útil porque las suscripciones de patrones amplios pueden sorprenderlo. Un puñado de servicios que usan PSUBSCRIBE * o PSUBSCRIBE events:* pueden recibir mucho más tráfico del previsto.

Si el tráfico de Pub/Sub es importante y de alto volumen, aíslelo. Una instancia de Redis dedicada para Pub/Sub protege su caché y almacén de sesiones de los problemas de búfer del suscriptor y los picos de transmisión. La instancia dedicada puede tener una configuración ajustada para Pub/Sub, preocupaciones de persistencia más cortas y monitoreo centrado en clientes y salida de red.

Mantenga los manejadores de mensajes cortos

Un suscriptor debe leer el mensaje, hacer una validación mínima y pasar el trabajo a otra cosa. Si el manejador llama a una base de datos, hace una solicitud HTTP, renderiza una plantilla o realiza un cálculo largo antes de leer el siguiente mensaje, puede convertirse en el consumidor lento que está tratando de evitar.

Mejor forma:

Mensaje Redis -> manejador ligero -> cola local -> grupo de trabajadores

La cola local puede ser una cola en proceso, un grupo de hilos, una cola de tareas asíncronas o un broker duradero si el trabajo debe sobrevivir a los reinicios del proceso. La idea clave es que la lectura de Redis no debe esperar a la dependencia descendente más lenta.

Si el trabajo debe ser confiable, Pub/Sub es la primitiva incorrecta. Redis Streams con grupos de consumidores le da entradas pendientes y acuses de recibo. Un broker dedicado puede ser mejor si necesita colas de mensajes fallidos, reintentos retrasados, prioridades o retención prolongada.

Una lista de verificación práctica para la solución de problemas

Cuando Pub/Sub parece poco saludable, generalmente trabajo en este orden:

  1. Confirme el canal manualmente con SUBSCRIBE y PUBLISH.
  2. Verifique CLIENT LIST para clientes Pub/Sub con omem alto.
  3. Inspeccione client-output-buffer-limit pubsub.
  4. Verifique la autenticación y los permisos de canal ACL.
  5. Confirme que los suscriptores usen conexiones dedicadas.
  6. Mate una conexión de suscriptor y verifique el comportamiento de reconexión y resuscripción.
  7. Verifique PUBSUB NUMSUB y PUBSUB NUMPAT para un fan-out inesperado.
  8. Decida si la carga de trabajo realmente necesita Streams o un broker dedicado.

Redis Pub/Sub es confiable solo en el sentido de que hace exactamente lo que promete: transmisión de mensajes en vivo a suscriptores actualmente conectados. La mayoría de los incidentes de producción provienen de esperar que se comporte como una cola duradera. Configure límites de búfer, aísle el tráfico pesado y diseñe suscriptores como si los mensajes pudieran perderse, porque a veces pueden.

Ejemplo de incidente: un panel ralentiza la caché

Imagine una instancia de Redis utilizada para claves de caché ordinarias y un pequeño canal Pub/Sub llamado metrics:live. Al principio, solo un panel interno se suscribe. Meses después, varios procesos de puerta de enlace del navegador también se suscriben, y uno de ellos comienza a enviar actualizaciones a través de conexiones WebSocket lentas. Redis no sabe que el navegador descendente es lento. Solo ve una conexión de suscriptor que no lee lo suficientemente rápido.

El primer síntoma puede no mencionar Pub/Sub en absoluto. Las solicitudes de aplicación que usan llamadas normales GET y SET comienzan a agotar el tiempo de espera. La memoria sube. El host de Redis parece ocupado, pero SLOWLOG no muestra un comando costoso obvio.

La pista útil está en CLIENT LIST:

flags=P sub=1 omem=25165824 cmd=subscribe

Eso le dice que un cliente Pub/Sub tiene un búfer de salida grande. Si el límite duro configurado es 32 MB, ese cliente está cerca de ser desconectado. Si el límite está deshabilitado, Redis puede seguir almacenando en búfer hasta que toda la instancia esté bajo presión de memoria.

La solución no es solo aumentar el límite. Aumentarlo puede ocultar el problema y permitir un pico de memoria peor la próxima vez. Una mejor respuesta es:

  1. Identificar el proceso suscriptor desde addr, name o metadatos del cliente.
  2. Matar al peor suscriptor si la memoria está en riesgo.
  3. Agregar o ajustar client-output-buffer-limit pubsub.
  4. Mover la entrega descendente lenta a una cola interna fuera del bucle de lectura de Redis.
  5. Considerar mover Pub/Sub de alto volumen a una instancia de Redis dedicada.

Los nombres de cliente hacen esto mucho más fácil. Muchas bibliotecas de Redis le permiten establecer un nombre de conexión. Use nombres como web-1:pubsub-cache-invalidations o dashboard:metrics-live para que CLIENT LIST apunte al propietario en lugar de solo mostrar una IP y puerto.

CLIENT SETNAME dashboard:metrics-live

Ese pequeño hábito de higiene convierte un incidente de memoria vago en una conversación directa con el equipo que posee el suscriptor.