Os topic aliases do MQTT 5 substituem um nome de topic longo por um número de 2 bytes pelo resto da conexão. O primeiro PUBLISH leva o topic e o alias. Cada PUBLISH seguinte envia um topic vazio mais o alias. Para testar, leia o Topic Alias Maximum que o broker concede no CONNACK, publique com e sem o nome do topic, verifique se os subscribers recebem o topic completo e reproduza os dois casos de erro: um alias fora do limite e um alias que o broker não conhece.
O mecanismo em si está em topic aliases no MQTT. Este artigo é o plano de testes.
Como os aliases são negociados
| Direção | Quem define o limite | Onde | Padrão |
|---|---|---|---|
| Cliente → broker | Broker | Topic Alias Maximum no CONNACK | 0 (sem aliases) |
| Broker → cliente | Cliente | Topic Alias Maximum no CONNECT | 0 (sem aliases) |
Regras que uma implementação correta segue:
- Valores de alias vão de 1 até o máximo negociado. 0 nunca é válido.
- Quem envia escolhe o mapeamento. Enviar um topic novo com um número de alias existente remapeia esse alias.
- Os mapeamentos são por conexão e por direção. Somem ao desconectar, mesmo com sessão persistente.
- Um PUBLISH com topic vazio precisa levar um alias já mapeado nesta conexão.
Quanto um alias economiza?
Pegue um topic de telemetria típico, factory/line-3/press-07/telemetry/vibration (43 caracteres), um payload de 20 bytes e QoS 1:
| Campo | Topic completo | Com alias |
|---|---|---|
| Cabeçalho fixo | 2 bytes | 2 bytes |
| Nome do topic (tamanho de 2 bytes + texto) | 45 bytes | 2 bytes (vazio) |
| Identificador do pacote | 2 bytes | 2 bytes |
| Propriedades (tamanho + Topic Alias) | 1 byte | 4 bytes |
| Payload | 20 bytes | 20 bytes |
| Total | 70 bytes | 30 bytes |
São 40 bytes economizados por mensagem, mais da metade do pacote. A uma mensagem por segundo, um dispositivo economiza cerca de 3,5 MB por dia. A economia só importa quando os topics são longos em relação aos payloads e quando bytes custam dinheiro ou bateria, como em links celulares, satelitais ou LPWAN. Numa LAN com payloads de 2 KB, é ruído. A calculadora de tamanho de pacote mostra os números para os seus topics, e overhead de banda no MQTT traz outras formas de reduzir o tráfego. O TLS adiciona o próprio overhead por registro, então meça no fio se o orçamento for apertado.
Teste 1: ler o máximo negociado
Antes de tudo, registre o que o broker concede. Brokers variam: alguns permitem muitos aliases, outros só alguns, e outros desativam o recurso (máximo 0). Um cliente que usa aliases sem verificar será desconectado em brokers que não permitem nenhum.
Com a CLI do Mosquitto, -d imprime o CONNACK, mas a saída de propriedades muda entre versões. Um script é mais confiável. O teste abaixo imprime o máximo primeiro e para se ele for 0.
Teste 2: publicações com alias chegam intactas
Este script com paho-mqtt 2.x publica uma mensagem com o topic completo e o alias 1, mais quatro com topic vazio e alias 1, e verifica se um subscriber normal recebe as cinco com o nome completo do topic. Depois reconecta e envia o alias antigo para ver como o broker reage.
import time, uuid
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes
HOST, PORT = "broker.connectmq.com", 1883
RUN = uuid.uuid4().hex[:8]
TOPIC = f"testmqtt/alias/{RUN}/factory/line-3/press-07/telemetry"
received = []
def client(cid, log):
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"{cid}-{RUN}", protocol=mqtt.MQTTv5)
c.on_connect = lambda cl, u, f, rc, props: log.update(max=getattr(props, "TopicAliasMaximum", 0))
c.on_disconnect = lambda cl, u, f, rc, props: log.update(disconnect=str(rc))
return c
def alias_props(alias):
p = Properties(PacketTypes.PUBLISH)
p.TopicAlias = alias
return p
# Subscriber: a normal client that does not accept aliases from the broker
sub_log = {}
sub = client("alias-sub", sub_log)
sub.on_message = lambda cl, u, msg: received.append((msg.topic, msg.payload.decode()))
sub.connect(HOST, PORT)
sub.loop_start()
sub.subscribe(TOPIC, qos=1)
# Test 1: negotiation + valid alias use
log = {}
pub = client("alias-pub", log)
pub.connect(HOST, PORT)
pub.loop_start()
time.sleep(1)
print("broker Topic Alias Maximum:", log.get("max"))
if not log.get("max"):
raise SystemExit("broker does not accept topic aliases from clients (maximum 0)")
pub.publish(TOPIC, "m0", qos=1, properties=alias_props(1)) # set alias 1 = TOPIC
for i in range(1, 5):
pub.publish("", f"m{i}", qos=1, properties=alias_props(1)) # topic omitted, alias only
time.sleep(2)
ok = received == [(TOPIC, f"m{i}") for i in range(5)]
print("PASS" if ok else "FAIL", "aliased messages arrive with the full topic:", len(received), "of 5")
pub.disconnect(); pub.loop_stop()
# Test 2: alias mappings do not survive a reconnect
received.clear()
log = {}
pub = client("alias-pub", log)
pub.connect(HOST, PORT)
pub.loop_start()
time.sleep(1)
pub.publish("", "after-reconnect", qos=1, properties=alias_props(1)) # unknown on this connection
time.sleep(2)
print("PASS" if not received else "FAIL", "stale alias after reconnect is not delivered;",
"broker reaction:", log.get("disconnect", "no DISCONNECT, message dropped"))
pub.disconnect(); pub.loop_stop()
# Test 3: alias above the negotiated maximum (only possible if the maximum is below 65535)
if log.get("max", 0) < 65535:
log2 = {}
bad = client("alias-bad", log2)
bad.connect(HOST, PORT)
bad.loop_start()
time.sleep(1)
bad.publish(TOPIC, "too-high", qos=1, properties=alias_props(log2["max"] + 1))
time.sleep(2)
print("broker reaction to alias > maximum:", log2.get("disconnect", "none"))
bad.loop_stop()
sub.disconnect(); sub.loop_stop()Uma execução típica imprime:
broker Topic Alias Maximum: 65535
PASS aliased messages arrive with the full topic: 5 of 5
PASS stale alias after reconnect is not delivered; broker reaction: no DISCONNECT, message droppedO paho-mqtt não gerencia aliases para você. Ele envia exatamente o alias que você define e não resolve aliases vindos do broker, então nunca anuncie um Topic Alias Maximum num subscriber paho. Isso também faz dele uma boa ferramenta para testes negativos. O paho recusa o alias 0 localmente (TopicAlias property value must be in the range 1-65535), então para testar como o broker trata o alias 0 você precisa de uma ferramenta de pacotes brutos.
Aliases automáticos com MQTT.js
O MQTT.js 5 pode atribuir aliases por você. Com autoAssignTopicAlias, ele mapeia topics para aliases pelo critério de uso menos recente (LRU), dentro do máximo do broker. Registre os pacotes para verificar o que vai pelo fio:
import mqtt from "mqtt";
const topic = "testmqtt/alias/js-demo/factory/line-3/press-07/telemetry";
const client = mqtt.connect("mqtt://broker.connectmq.com:1883", {
protocolVersion: 5,
clientId: `alias-js-${Math.random().toString(16).slice(2, 10)}`,
autoAssignTopicAlias: true, // let MQTT.js pick and reuse aliases
properties: { topicAliasMaximum: 10 }, // accept up to 10 aliases from the broker
});
// Log what actually goes over the wire
client.on("packetsend", (p) => {
if (p.cmd === "publish") console.log(`sent topic="${p.topic}" alias=${p.properties?.topicAlias}`);
});
client.on("connect", (connack) => {
console.log("broker Topic Alias Maximum:", connack.properties?.topicAliasMaximum ?? 0);
let n = 0;
const timer = setInterval(() => {
client.publish(topic, JSON.stringify({ n, vibration: Math.random() }), { qos: 1 });
if (++n === 5) {
clearInterval(timer);
setTimeout(() => client.end(), 500);
}
}, 300);
});
client.on("error", (err) => console.error("error:", err.message));Saída esperada: a primeira linha mostra o topic completo com alias=1, e as quatro seguintes mostram topic="" com alias=1. Se todas as linhas mostrarem o topic completo, o broker concedeu máximo 0 e o MQTT.js voltou silenciosamente para publicações normais. Esse é o comportamento correto, mas significa que você não está economizando nada. autoUseTopicAlias é a opção mais conservadora: só reutiliza aliases que você mesmo registrou.
Teste 3: os casos de erro
| O que você envia | O que a especificação diz | Erro que as pessoas procuram |
|---|---|---|
| Alias 0 | Erro de protocolo | DISCONNECT 0x94 Topic Alias invalid |
| Alias acima do máximo negociado | Erro de protocolo | DISCONNECT 0x94 Topic Alias invalid |
| Topic vazio, alias nunca mapeado | Erro de protocolo | DISCONNECT 0x82 Protocol Error |
| Qualquer alias quando o máximo é 0 | Erro de protocolo | 0x94, ou no cliente Sending Topic Alias out of range no MQTT.js |
| Alias antigo após reconectar | Igual a "nunca mapeado" | 0x82, ou a mensagem some em silêncio |
Nem todo broker reage do mesmo jeito. Alguns enviam o DISCONNECT com o reason code, outros fecham a conexão ou descartam aquela mensagem sem avisar. A execução acima mostra um broker que descartou a mensagem com alias antigo sem desconectar. Por isso seu teste deve validar o resultado que importa, a mensagem inválida não foi entregue com o topic errado, e registrar a reação do broker como informação. Decifre qualquer reason code com a consulta de reason codes. As correções para cada caso estão em erros Topic Alias invalid e erro de protocolo MQTT 0x82.
Verificar o lado do subscriber
Subscribers nunca deveriam perceber aliases, a não ser que tenham pedido. Abra o cliente MQTT online, assine testmqtt/alias/# e rode qualquer um dos scripts: toda mensagem deve mostrar o topic completo. Se um subscriber que não anunciou Topic Alias Maximum receber algum topic vazio, o broker está com defeito. É um bug raro, mas sério, que vale reportar.
Ver mensagens com alias chegando com o topic completo
Checklist
- Registre o Topic Alias Maximum de todo CONNACK e nunca o ultrapasse.
- Envie primeiro o topic completo com o alias, depois publicações só com alias, e valide que os subscribers veem topics completos.
- Reconecte e confirme que aliases antigos não são usados.
- Teste um alias acima do máximo e um alias não mapeado, e registre a reação do broker.
- Meça a economia real de bytes para os seus topics antes de tornar aliases um requisito.
Topic aliases são um ganho barato em links limitados e inúteis no resto. Teste a negociação e o caso de reconexão, e eles vão economizar banda sem virar o motivo de um dispositivo ficar mudo.
Perguntas frequentes
Qual é o valor padrão do Topic Alias Maximum?
0. Se o CONNECT ou o CONNACK não trouxer a propriedade Topic Alias Maximum, o receptor não aceita nenhum alias. Cada direção é negociada separadamente: o valor do cliente limita o broker e o valor do broker limita o cliente.
Topic aliases sobrevivem a uma reconexão?
Não. Os mapeamentos de alias existem só durante uma conexão de rede, mesmo com sessão persistente. Depois de cada reconexão, o cliente precisa enviar o nome completo do topic de novo antes de poder usar o alias.
Os subscribers enxergam o alias?
Só se pedirem. Aliases são por salto: o broker resolve o alias do publisher para o topic completo e só usa aliases com um subscriber que anunciou o próprio Topic Alias Maximum. A maioria dos subscribers simplesmente recebe o nome completo do topic.
