GuíasGuides · Sector 3Sector 3

RabbitMQ en la capa de integración de una plataforma clínica: colas, consumidores, dead-letter y los resultados que no llegan a la historia clínicaRabbitMQ in a clinical platform's integration layer: queues, consumers, dead-letter and the results that never reach the patient record

Cómo viaja un mensaje entre sistemas clínicos por RabbitMQ, qué mirar en la interfaz de gestión y con rabbitmqctl, qué alarmas poner sobre profundidad de cola y consumidores, para qué sirve una cola dead-letter, la cola que crece porque se paró un consumidor y los resultados de laboratorio no llegan a la historia clínica, cómo recuperarlo sin perder ni duplicar mensajes y cómo contárselo a los equipos clínicos.How a message travels between clinical systems through RabbitMQ, what to look at in the management UI and with rabbitmqctl, which alarms to set on queue depth and consumers, what a dead-letter queue is for, the queue that grows because a consumer stopped and lab results never reach the patient record, how to recover without losing or duplicating messages, and how to tell the clinical teams.

Qué vas a conseguir

Entender lo bastante la capa de mensajería de una plataforma clínica para vigilarla y para actuar cuando algo se atasca. Entre el sistema de laboratorio, el de admisiones y la historia clínica electrónica viajan mensajes (normalmente HL7) que pasan por un motor de integración y por RabbitMQ. Si una cola deja de vaciarse, en planta no se ven resultados. La guía cubre cómo se monta, qué se mira, qué alarmas poner y cómo recuperar un consumidor parado sin perder ni duplicar un solo mensaje.

Antes de empezar

  • Un usuario de la interfaz de gestión (puerto 15672) con la etiqueta monitoring para el equipo de operaciones; administrator solo para quien cambia la configuración.
  • Acceso al servidor de RabbitMQ para rabbitmqctl y rabbitmq-diagnostics (necesitan los permisos del usuario del servicio).
  • El mapa de interfaces: qué sistema publica en qué exchange, qué cola lee cada consumidor y quién es el responsable de cada uno (equipo de integración, proveedor, operaciones).
  • El procedimiento de contingencia clínico: qué hace el laboratorio o la planta cuando los resultados no llegan por la vía normal. No lo decide IT, pero IT tiene que saber cuándo activarlo.

1. Cómo viaja un resultado

Un resultado validado en el laboratorio sale como mensaje, pasa por el motor de integración y llega a la historia clínica:

  1. El motor de integración recibe el mensaje del sistema de laboratorio y lo publica en un exchange (clinico.resultados) con una clave de enrutado (lab.resultado).
  2. El exchange, según sus bindings, lo deja en una o varias colas: lab.resultados.hce para la historia clínica, y quizá otra para un sistema de archivo.
  3. Un consumidor (el servicio de integración de la historia clínica) lee de la cola, entrega el mensaje y, cuando la historia clínica lo ha aceptado, envía el ack. Solo entonces RabbitMQ lo borra.
PiezaEjemploLo que conviene saber
Virtual hostclinicoSepara entornos y permisos: producción y pruebas no comparten vhost
Exchangeclinico.resultados (topic)No guarda nada: si no hay cola enlazada, el mensaje se pierde
Colalab.resultados.hce (quorum)Duradera y replicada en el clúster; los mensajes, persistentes
Consumidorservicio de integración HCEAck manual y prefetch bajo (por ejemplo, 10)
Dead-letterlab.resultados.dlqDonde van los mensajes que no se pueden entregar

2. Mirar el estado: interfaz de gestión y rabbitmqctl

En la interfaz de gestión, la pestaña de colas muestra por cada una: Ready (esperando consumidor), Unacked (entregados y sin confirmar), Total, las tasas de entrada, entrega y ack, y el número de consumidores. Desde la línea de comandos:

rabbit01 · estado de las colas
rabbitmqctl list_queues -p clinico name type messages_ready messages_unacknowledged consumersTimeout: 60.0 seconds ...Listing queues for vhost clinico ...name                 type     messages_ready  messages_unacknowledged  consumerslab.resultados.hce   quorum   0               3                        1adt.eventos.hce      quorum   0               0                        1lab.resultados.dlq   quorum   0               0                        0rabbitmqctl list_consumers -p clinicorabbitmq-diagnostics alarmsNode rabbit@rabbit01 reported no alarms, local or clustered
  • Ready bajo, Unacked bajo y un consumidor: la cola se vacía al ritmo al que llega. Es lo normal.
  • Ready creciendo y 0 consumidores: nadie está leyendo. Es el caso del apartado 5.
  • Ready creciendo, Unacked igual al prefetch y la tasa de ack a cero: el consumidor está conectado pero atascado, esperando a la historia clínica o colgado.
  • rabbitmq-diagnostics alarms muestra las alarmas de memoria y disco. Con una activa, RabbitMQ bloquea a los publicadores: el problema ya no es una cola, es todo el nodo.

3. Qué monitorizar

  • Profundidad de cola (messages_ready) por encima de un umbral durante unos minutos. El umbral depende de la cola: una de resultados que suele estar a cero no es igual que una de carga nocturna.
  • Consumidores = 0 en las colas que siempre deben tener uno: alarma inmediata, sin esperar a que crezca.
  • Unacked atascado al nivel del prefetch sin acks durante varios minutos.
  • Mensajes en la dead-letter mayor que cero: cada uno es un resultado o un evento que no ha llegado.

El plugin de Prometheus (puerto 15692) expone métricas como rabbitmq_queue_messages_ready y rabbitmq_queue_consumers; para tenerlas por cola, el endpoint /metrics/per-object o /metrics/detailed. Desde ahí, Grafana o la herramienta de monitorización de la plataforma, con la alarma enviada a la guardia.

4. Dead-letter: los mensajes que no se pueden entregar

Un mensaje va a la dead-letter cuando el consumidor lo rechaza sin reencolar, cuando caduca por TTL, cuando la cola supera su longitud máxima o, en colas quorum, cuando supera su límite de entregas (un mensaje que hace fallar al consumidor una y otra vez). Se configura con una política:

rabbit01 · política de dead-letter para las colas de laboratorio
rabbitmqctl set_policy -p clinico DLX-lab "^lab\.resultados\.hce$" '{"dead-letter-exchange":"clinico.dlx"}' --apply-to queuesrabbitmqctl list_policies -p clinico

Con el exchange clinico.dlx enlazado a lab.resultados.dlq. Cada mensaje en la dead-letter lleva la cabecera x-death con el motivo y la cola de origen.

  • La dead-letter se revisa, no se purga. Cada mensaje es un dato clínico que no ha llegado a su destino.
  • Corregida la causa (un código de prueba que la historia clínica no conocía, un paciente sin dar de alta), el equipo de integración decide reprocesarlo. La interfaz de gestión puede mover los mensajes de vuelta a la cola original si están activos los plugins de shovel.

A una cola solo se le aplica una política (la de mayor prioridad). Una política nueva que coincida con una cola puede sustituir a la que ya tenía, con su TTL o su límite de longitud. Revisa list_policies antes de crear otra, y nunca pongas un TTL o una longitud máxima en una cola clínica sin dead-letter: los mensajes se descartarían en silencio.

5. El ticket: "los resultados de laboratorio no llegan a la historia clínica"

Martes, 10:35. Una supervisora de urgencias llama: hay analíticas validadas en el laboratorio que no aparecen en la historia clínica.

rabbit01 · la cola de resultados
rabbitmqctl list_queues -p clinico name messages_ready messages_unacknowledged consumersname                 messages_ready  messages_unacknowledged  consumerslab.resultados.hce   1842            0                        0adt.eventos.hce      0               0                        1lab.resultados.dlq   0               0                        0

1.842 resultados esperando y ningún consumidor. Los mensajes están a salvo en la cola: nada se ha perdido todavía.

  1. Avisa a los equipos clínicos primero (apartado 6). Ahora mismo hay pacientes cuyos resultados no se ven.
  2. Por qué se paró el consumidor: el servidor del servicio de integración se reinició a las 10:02 en la ventana de parches y el servicio no arrancó. La interfaz de gestión lo confirma: la cola empezó a crecer a las 10:02.
  3. Antes de arrancarlo: comprueba que la historia clínica acepta mensajes (su servicio de entrada responde) y avisa a su equipo de que va a llegar un volumen alto. El prefetch bajo hace que el consumidor procese a un ritmo que la historia clínica aguanta.
  4. Arranca el consumidor y mira cómo baja la cola: Ready disminuye, Unacked se mantiene en torno al prefetch y la tasa de ack es constante.
  5. Comprueba duplicados. Si el consumidor se cortó con mensajes sin confirmar, RabbitMQ los devuelve a la cola marcados como redelivered y se entregan otra vez. El consumidor debe ser idempotente: descartar un mensaje cuyo identificador de control HL7 (MSH-10) ya procesó.
  6. Concilia: número de resultados que el laboratorio envió entre las 10:02 y el final de la recuperación frente a los recibidos en la historia clínica, y dead-letter vacía o revisada.
  7. Cierra con los equipos clínicos y deja anotado: hora de inicio, de detección y de fin, número de mensajes retrasados, causa y acción. La acción obvia: el servicio en arranque automático y la alarma de consumidores = 0, que tenía que haber saltado a las 10:03 y no a las 10:35 por una llamada.

Lo que no se hace para "desatascar":

  • Purgar la cola: borra resultados clínicos sin vuelta atrás.
  • Borrar y recrear la cola, o reiniciar el nodo de RabbitMQ: no arregla un consumidor parado y arriesga lo que hay dentro.

6. Comunicar con los equipos clínicos

Lo que necesitan saber, en su idioma y no en el de la cola:

  • Qué está afectado: "los resultados de laboratorio validados desde las 10:02 no se están volcando en la historia clínica". Qué no lo está: imagen, prescripción, admisiones.
  • Que no se han perdido: están guardados y llegarán con su hora original de validación.
  • Qué hacer mientras tanto: consultar el visor del laboratorio y seguir el procedimiento de contingencia para los valores críticos, que el laboratorio comunica por teléfono.
  • Cuándo es la próxima actualización, aunque no haya novedades. No prometas una hora de solución que no controlas.
  • Al cerrar: "todos los resultados están ya en la historia clínica; los validados entre las 10:02 y las 11:10 llegaron con retraso". Si algún servicio debe revisar resultados de esa franja, que lo sepa ahora.

Siguientes pasos

  • La guía de Monitorización de este sector cubre cómo desplegar alarmas que lleguen a alguien y cómo responder a ellas.
  • La guía de Windows Server: actualizaciones planificadas sin cortar el servicio encaja con la causa del ticket: qué comprobar después de reiniciar un servidor que da servicio a otros.
  • Para coordinar un incidente con impacto clínico (roles, cronología, comunicaciones y revisión posterior), el repositorio 36-major-incident-command-kit de mi GitHub sigue esa idea.

¿Falta algo o en tu entorno es distinto? Escríbeme y lo corrijo.

What you'll get

Understanding the messaging layer of a clinical platform well enough to watch it and to act when something gets stuck. Between the lab system, the admissions system and the electronic patient record travel messages (usually HL7) that go through an integration engine and through RabbitMQ. If a queue stops draining, nobody on the ward sees results. The guide covers how it is put together, what to look at, which alarms to set and how to recover a stopped consumer without losing or duplicating a single message.

Before you start

  • A management UI user (port 15672) with the monitoring tag for the operations team; administrator only for whoever changes the configuration.
  • Access to the RabbitMQ server for rabbitmqctl and rabbitmq-diagnostics (they need the service user's permissions).
  • The interface map: which system publishes to which exchange, which queue each consumer reads and who owns each one (integration team, vendor, operations).
  • The clinical contingency procedure: what the lab or the ward does when results do not arrive the normal way. IT does not decide it, but IT has to know when to trigger it.

1. How a result travels

A result validated in the lab leaves as a message, goes through the integration engine and reaches the patient record:

  1. The integration engine receives the message from the lab system and publishes it to an exchange (clinico.resultados) with a routing key (lab.resultado).
  2. The exchange, according to its bindings, drops it into one or more queues: lab.resultados.hce for the patient record, and perhaps another for an archive system.
  3. A consumer (the patient record's integration service) reads from the queue, delivers the message and, once the patient record has accepted it, sends the ack. Only then does RabbitMQ delete it.
PieceExampleWhat is worth knowing
Virtual hostclinicoSeparates environments and permissions: production and test never share a vhost
Exchangeclinico.resultados (topic)Stores nothing: if no queue is bound, the message is lost
Queuelab.resultados.hce (quorum)Durable and replicated across the cluster; messages persistent
Consumerpatient record integration serviceManual ack and a low prefetch (say, 10)
Dead-letterlab.resultados.dlqWhere the messages that cannot be delivered go

2. Checking the state: management UI and rabbitmqctl

In the management UI, the queues tab shows for each one: Ready (waiting for a consumer), Unacked (delivered and not yet confirmed), Total, the incoming, deliver and ack rates, and the number of consumers. From the command line:

rabbit01 · queue status
rabbitmqctl list_queues -p clinico name type messages_ready messages_unacknowledged consumersTimeout: 60.0 seconds ...Listing queues for vhost clinico ...name                 type     messages_ready  messages_unacknowledged  consumerslab.resultados.hce   quorum   0               3                        1adt.eventos.hce      quorum   0               0                        1lab.resultados.dlq   quorum   0               0                        0rabbitmqctl list_consumers -p clinicorabbitmq-diagnostics alarmsNode rabbit@rabbit01 reported no alarms, local or clustered
  • Low Ready, low Unacked and one consumer: the queue drains as fast as messages arrive. That is normal.
  • Ready growing and 0 consumers: nobody is reading. That is the case in section 5.
  • Ready growing, Unacked equal to the prefetch and the ack rate at zero: the consumer is connected but stuck, waiting on the patient record or hung.
  • rabbitmq-diagnostics alarms shows the memory and disk alarms. With one active, RabbitMQ blocks publishers: the problem is no longer one queue, it is the whole node.

3. What to monitor

  • Queue depth (messages_ready) above a threshold for a few minutes. The threshold depends on the queue: a results queue that usually sits at zero is not the same as a night-time batch queue.
  • Consumers = 0 on the queues that must always have one: an immediate alarm, without waiting for it to grow.
  • Unacked stuck at the prefetch level with no acks for several minutes.
  • Messages in the dead-letter above zero: each one is a result or an event that has not arrived.

The Prometheus plugin (port 15692) exposes metrics such as rabbitmq_queue_messages_ready and rabbitmq_queue_consumers; to get them per queue, use the /metrics/per-object or /metrics/detailed endpoint. From there, Grafana or the platform's monitoring tool, with the alarm sent to the on-call person.

4. Dead-letter: the messages that cannot be delivered

A message goes to the dead-letter when the consumer rejects it without requeueing, when it expires through TTL, when the queue exceeds its maximum length or, on quorum queues, when it exceeds its delivery limit (a message that makes the consumer fail over and over). It is configured with a policy:

rabbit01 · dead-letter policy for the lab queues
rabbitmqctl set_policy -p clinico DLX-lab "^lab\.resultados\.hce$" '{"dead-letter-exchange":"clinico.dlx"}' --apply-to queuesrabbitmqctl list_policies -p clinico

With the clinico.dlx exchange bound to lab.resultados.dlq. Every message in the dead-letter carries the x-death header with the reason and the source queue.

  • The dead-letter is reviewed, not purged. Each message is clinical data that has not reached its destination.
  • Once the cause is fixed (a test code the patient record did not know, a patient not yet registered), the integration team decides whether to reprocess it. The management UI can move messages back to the original queue if the shovel plugins are enabled.

Only one policy applies to a queue (the one with the highest priority). A new policy that matches a queue can replace the one it already had, with its TTL or length limit. Check list_policies before creating another, and never put a TTL or a maximum length on a clinical queue without a dead-letter: messages would be dropped silently.

5. The ticket: "lab results are not reaching the patient record"

Tuesday, 10:35. An emergency department supervisor calls: there are blood tests validated in the lab that do not show up in the patient record.

rabbit01 · the results queue
rabbitmqctl list_queues -p clinico name messages_ready messages_unacknowledged consumersname                 messages_ready  messages_unacknowledged  consumerslab.resultados.hce   1842            0                        0adt.eventos.hce      0               0                        1lab.resultados.dlq   0               0                        0

1,842 results waiting and no consumer. The messages are safe in the queue: nothing has been lost yet.

  1. Tell the clinical teams first (section 6). Right now there are patients whose results cannot be seen.
  2. Why the consumer stopped: the integration service's server rebooted at 10:02 in the patching window and the service did not start. The management UI confirms it: the queue started growing at 10:02.
  3. Before starting it: check that the patient record accepts messages (its inbound service responds) and warn its team that a high volume is coming. The low prefetch keeps the consumer at a pace the patient record can take.
  4. Start the consumer and watch the queue drain: Ready goes down, Unacked stays around the prefetch and the ack rate is steady.
  5. Check for duplicates. If the consumer was cut off with unconfirmed messages, RabbitMQ returns them to the queue marked as redelivered and they are delivered again. The consumer must be idempotent: drop a message whose HL7 control ID (MSH-10) it has already processed.
  6. Reconcile: number of results the lab sent between 10:02 and the end of the recovery against those received in the patient record, and a dead-letter that is empty or reviewed.
  7. Close with the clinical teams and write it down: start, detection and end times, number of delayed messages, cause and action. The obvious action: the service set to start automatically, and the consumers = 0 alarm, which should have fired at 10:03 and not at 10:35 through a phone call.

What you do not do to "unblock" it:

  • Purge the queue: it deletes clinical results with no way back.
  • Delete and recreate the queue, or restart the RabbitMQ node: it does not fix a stopped consumer and puts what is inside at risk.

6. Communicating with the clinical teams

What they need to know, in their language rather than the queue's:

  • What is affected: "lab results validated since 10:02 are not being written to the patient record". What is not: imaging, prescribing, admissions.
  • That nothing has been lost: the results are stored and will arrive with their original validation time.
  • What to do in the meantime: check the lab viewer and follow the contingency procedure for critical values, which the lab phones through.
  • When the next update is, even if there is no news. Do not promise a fix time you do not control.
  • On closing: "all results are now in the patient record; those validated between 10:02 and 11:10 arrived late". If any department needs to review results from that window, they should know now.

Next steps

  • This sector's Monitoring guide covers how to deploy alarms that reach someone and how to respond to them.
  • The Windows Server: planned updates without interrupting the service guide fits the cause of this ticket: what to check after rebooting a server other systems depend on.
  • To coordinate an incident with clinical impact (roles, timeline, communications and the review afterwards), the 36-major-incident-command-kit repository on my GitHub follows that idea.

Is something missing, or different in your environment? Get in touch and I'll fix it.

Tech Career Lab · newsletter

¿Te ha servido? Cada dos semanas envío una idea práctica para perfiles IT: LinkedIn, proyectos, networking y procesos de selección.Found this useful? Every two weeks I send one practical idea for IT professionals: LinkedIn, projects, networking and hiring processes.

Apúntate y llévate las 33 ideasSign up and get the 33 ideas →