QoS MQTT (Quality of Service) é a garantia de entrega que um cliente solicita ao publicar ou assinar. Neste guia você vai ver exatamente quais pacotes QoS 0, 1 e 2 trocam, por que o QoS que chega a um subscriber pode ser menor do que o usado na publicação, como as sessões enfileiram mensagens para clientes offline e como testar tudo isso em Python no servidor MQTT público broker.connectmq.com.
Se o MQTT em si é novidade para você, comece por O Que É um Broker MQTT? e volte aqui depois.
O Que o QoS MQTT Realmente Garante
O MQTT roda sobre TCP, então os bytes não se perdem nem chegam fora de ordem dentro de uma mesma conexão. O QoS trata de outra coisa: o que acontece quando a conexão cai no meio de uma entrega e se o receptor pode receber a mesma mensagem duas vezes.
Dois pontos costumam ser mal compreendidos:
- O QoS é salto a salto, não ponta a ponta. O QoS do publisher rege o trecho do publisher até o broker. O QoS da assinatura rege o trecho do broker até o subscriber. O broker é o intermediário dos dois.
- QoS mais alto não é "melhor" por padrão. Cada nível adiciona pacotes, estado e latência. A escolha certa depende de quanto custa para você uma mensagem perdida ou duplicada.
| Nível | Nome | Garantia | Pacotes por mensagem (um trecho) | Pode haver duplicatas? |
|---|---|---|---|---|
| QoS 0 | At most once | Melhor esforço, sem confirmação | 1 | Não, mas pode haver perda |
| QoS 1 | At least once | Entregue, pode se repetir | 2 | Sim |
| QoS 2 | Exactly once | Entregue uma vez, sem duplicatas | 4 | Não |
QoS 0: At Most Once
QoS 0 é "envia e esquece". O remetente escreve um pacote PUBLISH no socket e imediatamente se esquece dele. Não há packet identifier nem confirmação.
Publisher Broker
| |
|------ PUBLISH (QoS 0) ---->|
| | (no reply, nothing stored)
Se a conexão TCP cair enquanto o pacote ainda está em um buffer do sistema operacional, a mensagem se perde e ninguém fica sabendo. Parece ruim, mas, para uma leitura de temperatura enviada a cada dois segundos, a próxima leitura substitui a perdida de qualquer forma. O QoS 0 oferece a menor latência e o menor overhead em dispositivos com recursos limitados.
QoS 1: At Least Once
Com QoS 1, o remetente atribui um packet identifier e guarda a mensagem até o receptor responder com um PUBACK contendo o mesmo identificador.
Publisher Broker
| |
|-- PUBLISH (QoS 1, id=7) -->| broker stores and forwards
|<------- PUBACK (id=7) -----|
| | publisher deletes message 7
Se o PUBACK nunca chegar (por exemplo, porque a conexão caiu), o remetente reenvia o PUBLISH com a flag DUP ativada após reconectar. O porém: o receptor pode já ter processado a primeira cópia, e então vê a mensagem duas vezes. É por isso que QoS 1 significa pelo menos uma vez.
O QoS 1 é o burro de carga da maioria dos sistemas IoT. Torne seus consumidores idempotentes (por exemplo, inclua um ID de mensagem ou um timestamp no payload e ignore repetições) e o QoS 1 geralmente será tudo de que você precisa.
QoS 2: Exactly Once
O QoS 2 usa um handshake em quatro etapas para que os dois lados concordem que a mensagem foi entregue exatamente uma vez.
Publisher Broker
| |
|--- PUBLISH (QoS 2, id=9) --->| broker stores id 9, does not forward twice
|<------- PUBREC (id=9) -------| "received"
|-------- PUBREL (id=9) ------>| "release it, I will not resend"
|<------- PUBCOMP (id=9) ------| "complete", both sides drop id 9
O que cada etapa faz:
- PUBLISH: o remetente armazena a mensagem e a envia com um packet identifier.
- PUBREC: o receptor registra o identificador. Se chegar um PUBLISH duplicado com o mesmo ID, ele é reconhecido e não é entregue de novo.
- PUBREL: o remetente descarta a mensagem e, a partir daí, só lembra do identificador.
- PUBCOMP: o receptor esquece o identificador. A troca termina.
O custo são duas viagens de ida e volta por mensagem e estado mantido nos dois lados até o fluxo terminar. Use QoS 2 quando uma duplicata causaria dano real e você não consegue tornar o consumidor idempotente, como um comando que incrementa um contador ou dispara um evento de cobrança.
Downgrade de QoS MQTT: Por Que Você Pode Não Receber o QoS Que Pediu
O QoS que um subscriber recebe não é simplesmente o QoS usado pelo publisher. Três números interagem.
QoS efetivo = min(QoS da publicação, QoS da assinatura)
Quando o broker encaminha uma mensagem, ele usa o menor valor entre o QoS do PUBLISH original e o QoS concedido para a assinatura.
| QoS do publisher | QoS da assinatura | Entregue ao subscriber |
|---|---|---|
| 0 | 2 | 0 |
| 1 | 2 | 1 |
| 2 | 1 | 1 |
| 2 | 0 | 0 |
| 2 | 2 | 2 |
Ou seja, se você precisa de entrega exactly-once de ponta a ponta, tanto o publisher quanto todos os subscribers precisam usar QoS 2. Uma única assinatura com QoS 0 transforma, sem alarde, o fluxo daquele subscriber em melhor esforço.
O Maximum QoS do broker (MQTT 5)
No MQTT 5, o broker pode anunciar um Maximum QoS no pacote CONNACK. Se essa propriedade não vier, o cliente pode assumir que QoS 2 é suportado. Se vier, o cliente não deve publicar acima desse nível, e o broker concederá assinaturas no máximo nesse nível (o SUBACK informa o QoS concedido).
No ConnectMQ, isso depende de onde você se conecta:
| Ambiente | Maximum QoS |
|---|---|
| Servidor MQTT público (broker.connectmq.com) | QoS 2 |
| Plano Serverless (Free) | QoS 1 |
| Plano Serverless (Paid) | QoS 2 |
| Dedicated, BYOC, Self Managed Enterprise | QoS 2 |
Sempre confira o QoS concedido no SUBACK em vez de presumir. A maioria das bibliotecas cliente o expõe no callback de subscribe. A especificação do MQTT 5 em docs.oasis-open.org descreve as regras exatas. Para conhecer outras propriedades do CONNACK, veja Recursos do MQTT 5.
Sessões, Session Expiry e Fila Offline
O QoS só protege mensagens através de uma desconexão se a sessão sobreviver a essa desconexão. A sessão é o estado que o broker mantém para um cliente: suas assinaturas, as mensagens QoS 1/2 em trânsito e as mensagens enfileiradas enquanto ele está offline.
- O MQTT 3.1.1 usa a flag Clean Session.
cleanSession=truedescarta a sessão na desconexão.cleanSession=falsepede ao broker que a mantenha. - O MQTT 5 divide isso em Clean Start (começar do zero na conexão ou não) e Session Expiry Interval (por quantos segundos a sessão vive após a desconexão).
Enquanto existir uma sessão persistente e o cliente estiver offline, o broker enfileira as mensagens QoS 1 e QoS 2 que correspondem às suas assinaturas. Mensagens QoS 0 não são enfileiradas para clientes offline. Quando o cliente reconecta com o mesmo client ID e sem clean start, as mensagens enfileiradas são entregues.
Subscriber (id=sensor-reader) Broker
|-- CONNECT clean_start=0, expiry=600 -->|
|-- SUBSCRIBE plant/# QoS 1 ------------>|
| ... connection lost ... |
| | PUBLISH plant/a QoS 1 -> queued
| | PUBLISH plant/b QoS 0 -> dropped
|-- CONNECT clean_start=0 --------------->|
|<-- CONNACK session_present=1 -----------|
|<-- PUBLISH plant/a ---------------------|
No ConnectMQ, o session expiry tem um teto por plano, e no servidor MQTT público as sessões vivem por até 30 minutos. Observe também que as sessões ficam em memória: elas não sobrevivem a um reinício do broker, então projete os fluxos críticos para tolerar uma reconexão com session_present=0.
Como Escolher o Nível de QoS MQTT Certo
| Caso de uso | QoS recomendado | Por quê |
|---|---|---|
| Telemetria de alta frequência (temperatura, vibração) | 0 | A próxima amostra substitui a perdida; menor overhead |
| Status de dispositivo, alarmes, eventos | 1 | Precisa chegar; duplicatas são fáceis de ignorar |
| Comandos para atuadores (idempotentes: "ajustar válvula para 40%" em uma estação de saneamento) | 1 | Repetir o mesmo comando não causa problema |
| Comandos não idempotentes ("incrementar contador", "dispensar") | 2 | Uma duplicata mudaria o resultado |
| Eventos de cobrança, medição ou transações | 2 (ou 1 com IDs de deduplicação) | Contabilização exactly-once |
Uma regra prática: use QoS 1 como padrão com consumidores idempotentes, desça para QoS 0 em dados de alta taxa que você pode se dar ao luxo de perder e reserve o QoS 2 para os poucos tipos de mensagem em que uma duplicata é um bug.
Mão na Massa: QoS 1 e QoS 2 do MQTT em Python
O script a seguir usa paho-mqtt 2.x e o servidor MQTT público broker.connectmq.com (sem usuário nem senha). Ele assina com QoS 2, publica uma mensagem com QoS 1 e outra com QoS 2 e registra as confirmações. Instale a biblioteca com pip install "paho-mqtt>=2.0".
import time
import uuid
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
UID = uuid.uuid4().hex[:8]
TOPIC = f"connectmq/demo/{UID}/qos"
def on_connect(client, userdata, flags, reason_code, properties):
print("CONNACK:", reason_code)
if properties is not None and hasattr(properties, "MaximumQoS"):
print("Broker Maximum QoS:", properties.MaximumQoS)
client.subscribe(f"{TOPIC}/#", qos=2)
def on_subscribe(client, userdata, mid, reason_code_list, properties):
print("SUBACK granted:", [rc.value for rc in reason_code_list])
def on_message(client, userdata, msg):
print(f"Received {msg.topic} QoS={msg.qos} payload={msg.payload.decode()}")
def on_publish(client, userdata, mid, reason_code, properties):
print(f"Publish flow complete for mid={mid}")
client = mqtt.Client(
mqtt.CallbackAPIVersion.VERSION2,
client_id=f"qos-demo-{UID}",
protocol=mqtt.MQTTv5,
)
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_message = on_message
client.on_publish = on_publish
client.connect(BROKER, 1883, keepalive=30)
client.loop_start()
time.sleep(2)
client.publish(f"{TOPIC}/one", "hello at least once", qos=1).wait_for_publish()
client.publish(f"{TOPIC}/two", "hello exactly once", qos=2).wait_for_publish()
time.sleep(2)
client.loop_stop()
client.disconnect()
Com QoS 1, on_publish dispara quando o PUBACK chega. Com QoS 2, ele só dispara depois do PUBCOMP, no fim do handshake em quatro etapas. Se nenhuma linha de Maximum QoS for impressa, o broker não enviou a propriedade, o que significa que QoS 2 é permitido.
Observe os pacotes com o CLI do Mosquitto
As ferramentas de linha de comando do Eclipse Mosquitto imprimem cada pacote com -d, o que torna visíveis os fluxos acima. Em um terminal:
mosquitto_sub -h broker.connectmq.com -p 1883 -i qos-sub-a1b2 -t "connectmq/demo/a1b2/#" -q 2 -v -d
Em um segundo terminal, publique com QoS 1 e depois com QoS 2:
mosquitto_pub -h broker.connectmq.com -p 1883 -i qos-pub-a1b2 -t "connectmq/demo/a1b2/q1" -q 1 -m "hello1" -d
mosquitto_pub -h broker.connectmq.com -p 1883 -i qos-pub-a1b2 -t "connectmq/demo/a1b2/q2" -q 2 -m "hello2" -d
A saída do publisher mostra exatamente as sequências descritas antes:
Client qos-pub-a1b2 sending PUBLISH (d0, q1, r0, m1, 'connectmq/demo/a1b2/q1', ... (6 bytes))
Client qos-pub-a1b2 received PUBACK (Mid: 1, RC:0)
...
Client qos-pub-a1b2 sending PUBLISH (d0, q2, r0, m1, 'connectmq/demo/a1b2/q2', ... (6 bytes))
Client qos-pub-a1b2 received PUBREC (Mid: 1)
Client qos-pub-a1b2 sending PUBREL (m1)
Client qos-pub-a1b2 received PUBCOMP (Mid: 1, RC:0)
Troque a1b2 por uma string aleatória sua. Tudo no servidor MQTT público pode ser lido por qualquer pessoa, então nunca publique dados sensíveis ali. Para experimentar downgrades, assine com -q 0 e publique com -q 2: a saída de debug do subscriber mostrará q0 no PUBLISH entregue. Mais dicas, e o lembrete de que ele serve apenas para testes e protótipos, no guia do servidor MQTT público (gratuito).
Erros Comuns com QoS
- Presumir QoS 2 de ponta a ponta enquanto um subscriber usa QoS 0. O elo mais fraco vence.
- Usar QoS 2 para telemetria de alta taxa. Quatro pacotes por mensagem por trecho se acumulam rápido, e a janela de mensagens em trânsito (Receive Maximum) enche.
- Client IDs aleatórios com sessões persistentes. A fila offline só funciona se o cliente reconectar com o mesmo client ID. Um novo ID aleatório cria uma sessão nova e vazia.
- Ignorar o QoS concedido no SUBACK. Em um plano limitado a QoS 1, uma assinatura QoS 2 é concedida como QoS 1.
Comece com o ConnectMQ
O servidor MQTT público é ótimo para aprender QoS MQTT, mas tráfego de produção, como a telemetria de uma usina ou de uma fazenda, deve ficar em um broker MQTT gerenciado privado com credenciais. Cadastre-se gratuitamente em app.connectmq.com no plano Serverless (Free), sem cartão de crédito, crie um broker e use o usuário, a senha e o client ID gerados. Faça upgrade para Serverless (Paid) ou Dedicated quando precisar de QoS 2, mais conexões ou mensagens maiores.
Perguntas frequentes
Qual é o nível de QoS MQTT padrão?
A maioria das bibliotecas cliente usa QoS 0 por padrão tanto para publish quanto para subscribe. Defina o QoS explicitamente no seu código para que o comportamento fique claro para quem o ler.
O QoS 2 deixa o MQTT mais lento?
Sim. O QoS 2 precisa de quatro pacotes e duas viagens de ida e volta por mensagem em cada trecho, contra dois pacotes no QoS 1 e um no QoS 0. Para a maior parte da telemetria, QoS 0 ou 1 oferece melhor throughput.
Por que meu subscriber recebeu QoS 1 se publiquei com QoS 2?
O QoS entregue é o mínimo entre o QoS da publicação e o QoS concedido à assinatura. Sua assinatura foi concedida com QoS 1, seja porque você pediu isso, seja porque o Maximum QoS do broker é 1, como no plano Serverless (Free) do ConnectMQ.
Mensagens QoS 0 são enfileiradas para clientes offline?
Não. Os brokers só enfileiram mensagens QoS 1 e QoS 2 para clientes com sessão persistente. Mensagens QoS 0 destinadas a um cliente offline são descartadas.
Posso usar QoS 2 no broker.connectmq.com?
Sim. O servidor MQTT público, de acesso anônimo, suporta QoS 0, 1 e 2. Brokers privados suportam QoS 2 no Serverless (Paid) e nos planos superiores, enquanto o plano Serverless (Free) é limitado a QoS 1.



