Fundamentos de MQTT

Buenas prácticas del client ID de MQTT: unicidad, formato y takeovers

El client ID es la forma en que un broker MQTT sabe quién eres. Si te equivocas, los dispositivos se expulsan entre sí o pierden sus sesiones. Estas son las reglas, formatos y pasos de depuración que funcionan.

Ilustración del client ID de MQTT con dos dispositivos que usan el mismo identificador y compiten por una única conexión al broker
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

El client ID de MQTT es el identificador que un cliente envía en el CONNECT, y el broker lo usa para distinguir conexiones y sesiones. Esta guía explica por qué dos clientes con el mismo ID se desconectan mutuamente una y otra vez, cómo diseñar formatos de ID que escalen desde un prototipo hasta una flota y cómo diagnosticar rápidamente los takeovers cuando pruebas en un broker público.

Para qué se usa el client ID de MQTT

Todo paquete CONNECT lleva un campo Client Identifier. El broker MQTT lo usa para tres cosas:

  1. Unicidad de la conexión. Solo puede haber una conexión de red activa por client ID a la vez.
  2. Búsqueda de la sesión. El estado de la sesión persistente (suscripciones y mensajes encolados) se guarda asociado al client ID.
  3. Operaciones. Los logs, los eventos de conexión y los topics de monitorización se refieren a los clientes por su ID.

Por sí solo, no es una credencial de autenticación. Cualquiera que pueda conectarse puede reclamar cualquier ID, salvo que el broker vincule los IDs a credenciales. En los brokers privados de ConnectMQ, los client IDs están vinculados a las credenciales emitidas en el portal, así que otro usuario no puede reutilizar el ID de tu dispositivo.

Por qué los client IDs duplicados se expulsan entre sí

La especificación es explícita: si un cliente se conecta con un ID que ya está conectado, el broker debe desconectar al cliente existente. Esto se llama session takeover. Existe para que un dispositivo que perdió su conexión sin avisar pueda reconectarse de inmediato sin esperar a que expire el socket antiguo.

El problema aparece cuando dos clientes distintos comparten un ID. La secuencia es esta:

  1. El cliente A se conecta como sensor-01.
  2. El cliente B se conecta como sensor-01. El broker desconecta a A.
  3. La reconexión automática de A se dispara y se conecta como sensor-01. El broker desconecta a B.
  4. B se reconecta... y el bucle se repite cada pocos segundos.

Los síntomas incluyen logs constantes de conexión y desconexión, mensajes perdidos, mensajes de will disparados repetidamente y un indicador "online/offline" que parpadea. En MQTT 5, el cliente expulsado recibe un DISCONNECT con el reason code 0x8E (Session taken over), lo que facilita mucho el diagnóstico frente a 3.1.1, donde el socket simplemente se cierra.

Causas habituales

  • La misma imagen de firmware grabada en muchas placas con un ID fijo en el código.
  • Código de ejemplo copiado y pegado con un ID como test o esp32client.
  • Un servicio que se ejecuta en varios contenedores o pods con el mismo ID configurado.
  • Un desarrollador que ejecuta el mismo script en dos terminales.
  • Una herramienta MQTT de escritorio y un dispositivo que usan el ID del dispositivo.

Cómo diseñar un formato de client ID de MQTT

Los buenos IDs son únicos, estables, legibles y seguros para cualquier broker.

Reglas de formato

Regla Por qué
Usa solo a-z, A-Z, 0-9, - y _ Todos los brokers deben aceptar estos caracteres
Mantenlo corto, idealmente de 23 caracteres o menos MQTT 3.1.1 solo garantiza soporte hasta 23 bytes; la mayoría de los brokers permiten más
No lo dejes nunca vacío en producción Los IDs vacíos impiden las sesiones persistentes y muchos brokers los rechazan
Hazlo estable en dispositivos con sesiones Un ID que cambia implica una sesión nueva cada vez
Hazlo único por instancia en ejecución Evita los bucles de takeover
Evita secretos y datos personales Los IDs aparecen en logs y en la monitorización

Patrones que funcionan

  • Dispositivos: un prefijo más un identificador derivado del hardware, como env- más los últimos bytes de la dirección MAC (env-a4cf12b3), o un número de serie de tu base de datos de fabricación.
  • Servicios backend: nombre del servicio más identificador de instancia, como alarm-worker-0 a partir del ordinal de un StatefulSet. Si las instancias son totalmente intercambiables y no necesitan sesiones, un sufijo aleatorio sirve.
  • Dashboards de navegador: un prefijo más una cadena aleatoria por pestaña, con sesiones limpias, porque nadie necesita reanudar la sesión de un dashboard.
  • Scripts de prueba: un sufijo aleatorio en cada ejecución, por ejemplo test- más 6 caracteres hexadecimales aleatorios.

Cómo generar IDs en código

Python, usando la dirección MAC para un ID de dispositivo estable o un token aleatorio para clientes de prueba:

import secrets
import uuid
import paho.mqtt.client as mqtt

def device_client_id(prefix: str = "env") -> str:
    mac = uuid.getnode()            # dirección hardware de 48 bits como entero
    return f"{prefix}-{mac:012x}"   # p. ej. env-a4cf12b3c4d5

def test_client_id(prefix: str = "test") -> str:
    return f"{prefix}-{secrets.token_hex(3)}"  # p. ej. test-9f2c1a

client_id = device_client_id()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                     client_id=client_id,
                     protocol=mqtt.MQTTv5)
print("Using client ID:", client_id)

ESP32 con PubSubClient, derivando el ID de la MAC del efuse:

#include <WiFi.h>
#include <PubSubClient.h>

WiFiClient net;
PubSubClient mqttClient(net);

String makeClientId() {
  uint64_t mac = ESP.getEfuseMac();
  char id[24];
  snprintf(id, sizeof(id), "env-%04x%08x",
           (uint16_t)(mac >> 32), (uint32_t)mac);
  return String(id);
}

void connectMqtt() {
  String clientId = makeClientId();
  mqttClient.setServer("broker.connectmq.com", 1883);
  while (!mqttClient.connected()) {
    if (mqttClient.connect(clientId.c_str())) {
      Serial.println("Connected as " + clientId);
    } else {
      Serial.printf("Failed, state=%d. Retrying in 5 s\n", mqttClient.state());
      delay(5000);
    }
  }
}

La configuración completa de la placa, Wi-Fi incluido, está en el tutorial de MQTT con ESP32.

Client IDs vacíos e identificadores asignados

MQTT 3.1.1 permite un client ID de longitud cero solo con clean session a true; en ese caso el broker inventa uno internamente. MQTT 5 va más allá: el broker devuelve un Assigned Client Identifier en el CONNACK para que el cliente conozca su nombre. Es práctico para clientes desechables, pero descarta las sesiones persistentes, porque no puedes reconectarte con una identidad que no controlas. Muchos brokers, incluido el servidor MQTT público gratuito de ConnectMQ, simplemente exigen un ID no vacío, que en cualquier caso es el hábito más seguro.

Cómo detectar un takeover de client ID

Aquí tienes una prueba reproducible en el servidor MQTT público gratuito. Usa tu propio sufijo aleatorio y recuerda que cualquiera puede leer el área pública: sirve para pruebas y prototipos, no para datos de producción.

  1. En el primer terminal, conéctate con un ID y salida de depuración:
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 \
  -i dup-demo-k7q1 -d -t 'connectmq/demo/k7q1/#'
  1. En el segundo terminal, ejecuta exactamente el mismo comando.
  2. El primer terminal se desconecta de inmediato; con -d puedes ver cómo el broker cierra su conexión. Según tu versión de mosquitto, o bien termina o bien se reconecta, y en ese caso los dos terminales empiezan a expulsarse mutuamente, que es exactamente el bucle descrito antes.
  3. En un tercer terminal, observa cómo ve el broker a ese cliente:
mosquitto_sub -h broker.connectmq.com -p 1883 -i watcher-k7q1 -v \
  -t '$SYS/broker/connection/dup-demo-k7q1/state'

En producción, una lista de comprobación rápida cuando los dispositivos se conectan y desconectan sin parar:

  1. Recopila los client IDs de todos los dispositivos sospechosos y busca duplicados.
  2. Comprueba si los reason codes de desconexión muestran 0x8E (session taken over).
  3. Compara los cambios de estado de conexión con las marcas de tiempo de reconexión de cada dispositivo; los patrones alternos delatan a una pareja que se disputa un ID.
  4. Cierra las herramientas de escritorio o sesiones de VS Code que puedan estar usando el ID de un dispositivo.
  5. Si las desconexiones continúan con IDs únicos, revisa en su lugar el keep alive y los timeouts de red.

Client IDs de MQTT en producción

Unos pocos hábitos operativos evitan la mayoría de los incidentes con client IDs. Genera los IDs en el momento del aprovisionamiento y no en tiempo de ejecución, para que el mismo dispositivo reciba siempre el mismo ID incluso tras un restablecimiento de fábrica. Guarda el ID junto a las credenciales del dispositivo para que el equipo de soporte pueda relacionar las entradas de log con el hardware. Cuando sustituyas una unidad averiada, decide de forma deliberada si el nuevo dispositivo hereda el ID antiguo (y su sesión) o recibe uno nuevo. En los servicios, haz que el identificador de instancia provenga de tu orquestador y no de un archivo de configuración compartido por todas las réplicas.

A medida que las flotas crecen, trata los client IDs como parte de tu proceso de aprovisionamiento: regístralos junto a los números de serie, derívalos del hardware y nunca reutilices un ID para otro dispositivo físico. Si estás aprendiendo MQTT desde cero, el tutorial de MQTT explica cómo encajan IDs, sesiones y topics.

Gestiona los client IDs de forma segura con ConnectMQ

ConnectMQ es un broker MQTT gestionado que emite un nombre de usuario, una contraseña y un client ID para cada broker que creas, y vincula los client IDs a esas credenciales para que otros tenants no puedan secuestrarlos. El estado de las conexiones se ve en directo en el portal, lo que hace que los bucles de takeover sean fáciles de detectar. Crea un broker Serverless gratuito sin tarjeta de crédito y conéctate usando YOUR_USERNAME, YOUR_PASSWORD y YOUR_CLIENT_ID.

Preguntas frecuentes

¿Qué ocurre si dos clientes MQTT usan el mismo client ID?

El broker desconecta al cliente que ya estaba conectado y se queda con el nuevo. Si ambos clientes se reconectan automáticamente, siguen expulsándose el uno al otro en un bucle.

¿Qué longitud puede tener un client ID de MQTT?

MQTT 3.1.1 garantiza soporte para entre 1 y 23 bytes de letras y dígitos, y los brokers pueden permitir IDs más largos y más caracteres. Mantener IDs cortos, alfanuméricos y con guiones es la opción más portable.

¿Debe ser aleatorio el client ID de MQTT?

Usa un ID aleatorio para clientes efímeros o intercambiables que no necesiten sesiones. Los dispositivos y servicios que dependen de sesiones persistentes necesitan un ID estable y único que no cambie entre reconexiones.

¿Es el client ID una credencial de seguridad?

No. Trátalo solo como un identificador y autentícate siempre con usuario y contraseña o con certificados; los brokers que vinculan los IDs a credenciales, como hace ConnectMQ, añaden protección contra la suplantación.

Artículos relacionados

¿Quién inventó MQTT? La historia, de los oleoductos al IoT

MQTT lo inventaron en 1999 Andy Stanford-Clark, de IBM, y Arlen Nipper, de Arcom, para monitorizar oleoductos vía satélite. Sigue su camino hacia el open source, OASIS, ISO y los brokers MQTT gestionados en la nube de hoy.

8 min de lectura

Por qué tu cliente MQTT se desconecta constantemente (y cómo solucionarlo)

Los bucles de reconexión son uno de los problemas más habituales de MQTT. Conoce las cinco causas típicas, desde los client ID duplicados hasta los timeouts de keep alive y los paquetes demasiado grandes, y sigue un flujo de diagnóstico que localiza la tuya.

9 min de lectura

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.