O receive maximum MQTT é a configuração do MQTT 5 que informa ao outro lado quantas mensagens QoS 1 e QoS 2 você aceita ter "em trânsito", sem confirmação, ao mesmo tempo. Neste guia você vai aprender como essa janela de controle de fluxo funciona nas duas direções, por que ela importa para microcontroladores e rajadas após reconexões e como configurá-la no mosquitto, em Python e em Node.js.
Qual Problema o Receive Maximum Resolve
O MQTT 3.1.1 não tinha controle de fluxo padronizado. Um broker podia enviar mensagens QoS 1 a um cliente tão rápido quanto a conexão TCP permitisse, e o cliente precisava armazenar cada uma delas em buffer até enviar o PUBACK. Isso funciona em um servidor, mas um microcontrolador com poucos kilobytes de RAM livre pode ficar sem memória, perder mensagens ou travar. O problema é pior logo após uma reconexão, quando uma sessão persistente libera de uma só vez um acúmulo de mensagens enfileiradas.
Brokers e bibliotecas inventaram suas próprias configurações de "max in-flight" para lidar com isso, mas elas não eram negociadas, então nenhum dos lados conhecia o limite do outro. O MQTT 5 padronizou a ideia como a property Receive Maximum.
Como Funciona a Janela de Receive Maximum MQTT
O Receive Maximum é um inteiro de dois bytes, de 1 a 65.535, que cada lado pode declarar:
- O cliente o envia no CONNECT: "Broker, não me envie mais do que N pacotes PUBLISH QoS 1/2 sem confirmação."
- O broker o envia no CONNACK: "Cliente, não me envie mais do que N pacotes PUBLISH QoS 1/2 sem confirmação."
Se a property estiver ausente, o valor padrão é 65.535, o que na prática significa "sem limite". O valor 0 não é permitido.
O Que Conta como Em Trânsito
| Tipo de pacote | Conta na janela? | Liberado quando |
|---|---|---|
| PUBLISH QoS 0 | Não | Não se aplica |
| PUBLISH QoS 1 | Sim | PUBACK recebido |
| PUBLISH QoS 2 | Sim | PUBCOMP recebido (ou PUBREC com reason code de erro) |
| Retained message enviada na assinatura | Sim, se entregue em QoS 1 ou 2 | Confirmação normal |
| Mensagens offline enfileiradas | Sim, em QoS 1 ou 2 | Confirmação normal |
O remetente mantém um contador (a especificação o chama de send quota). Cada PUBLISH QoS 1 ou 2 o decrementa; cada confirmação final o incrementa. Quando ele chega a zero, o remetente precisa parar de enviar mensagens QoS 1 e 2 e esperar. Ele ainda pode enviar mensagens QoS 0, e a especificação permite que continue enviando PINGREQ, então o keep alive não é afetado.
O Que Acontece se o Limite For Excedido
Se um remetente ignorar a janela e o receptor receber mais mensagens QoS 1/2 não confirmadas do que anunciou, o receptor deve desconectar com o reason code 0x93 (Receive Maximum exceeded). Na prática, isso é raro, porque as bibliotecas mais usadas aplicam a janela automaticamente. Quando aparece, quase sempre indica uma implementação de cliente própria ou muito antiga.
Por Que Isso Importa para Dispositivos Lentos
Considere um ESP32 que se reconecta após uma queda de Wi-Fi de 10 minutos. A sessão dele guarda 200 atualizações de configuração QoS 1 enfileiradas. Sem controle de fluxo, o broker pode enviar as 200 de uma vez. Com um receive maximum de 5, o broker envia cinco, espera os PUBACKs e envia mais à medida que cada confirmação chega. O uso de memória fica limitado, o processamento acompanha o ritmo e o dispositivo continua responsivo.
A janela também oferece back-pressure natural para consumidores de backend: se um worker só envia o PUBACK depois de gravar a mensagem no banco de dados, um receive maximum pequeno impede que o broker acumule mais trabalho do que o worker consegue processar. Isso complementa bem as shared subscriptions MQTT, em que vários workers dividem um mesmo fluxo e a janela de cada worker limita a sua parte do trabalho em trânsito.
Configurando o Receive Maximum nos Clientes
Teste no servidor MQTT público em broker.connectmq.com com um prefixo único como connectmq/demo/r2m6/.... A área pública é aberta a todos, então use payloads fictícios.
mosquitto_sub
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 \
-i recvmax-demo-r2m6 -q 1 -v -d \
-D connect receive-maximum 5 \
-t 'connectmq/demo/r2m6/#'
A saída de -d mostra as properties do CONNECT que você enviou e as properties do CONNACK que o broker devolveu, incluindo o receive maximum dele, se tiver definido um.
Python (paho-mqtt 2.x)
import time
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes
def on_connect(client, userdata, flags, reason_code, properties):
server_rm = getattr(properties, "ReceiveMaximum", 65535)
print("Connected:", reason_code, "| broker receive maximum:", server_rm)
client.subscribe("connectmq/demo/r2m6/#", qos=1)
def on_message(client, userdata, msg):
# Simula um dispositivo lento; o paho envia o PUBACK depois que este callback retorna
time.sleep(0.5)
print("Handled", msg.topic, msg.payload.decode())
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id="recvmax-py-r2m6",
protocol=mqtt.MQTTv5)
client.on_connect = on_connect
client.on_message = on_message
# Limita o que *nós* enviamos simultaneamente (janela de saída)
client.max_inflight_messages_set(10)
# Pede ao broker que limite o que *ele* nos envia (janela de entrada)
props = Properties(PacketTypes.CONNECT)
props.ReceiveMaximum = 5
client.connect("broker.connectmq.com", 1883, keepalive=60, properties=props)
client.loop_forever()
Publique uma rajada a partir de outro terminal e veja as mensagens chegarem em ritmo constante:
for i in $(seq 1 30); do
mosquitto_pub -h broker.connectmq.com -p 1883 -i burst-r2m6 \
-q 1 -t 'connectmq/demo/r2m6/burst' -m "msg $i"
done
Observe que o time.sleep dentro do callback também bloqueia o loop de rede do paho. Isso não é problema nesta demonstração, mas em produção mova o trabalho lento para uma thread separada e mantenha o loop livre, ou os pings de keep alive vão atrasar.
Node.js (MQTT.js v5)
const mqtt = require("mqtt");
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId: "recvmax-node-r2m6",
protocolVersion: 5,
properties: { receiveMaximum: 5 },
});
client.on("connect", (connack) => {
console.log("broker properties:", connack.properties);
client.subscribe("connectmq/demo/r2m6/#", { qos: 1 });
});
client.on("message", (topic, payload) => {
console.log(topic, payload.toString());
});
Escolhendo um Valor de Receive Maximum
| Cliente | Valor sugerido | Por quê |
|---|---|---|
| Microcontrolador (ESP32, ESP8266) | 1 a 10 | RAM limitada, processamento sequencial |
| Gateway Raspberry Pi | 10 a 50 | Mais memória, CPU ainda modesta |
| Worker de backend gravando em banco de dados | Aproximadamente o número de gravações paralelas que ele sustenta | O back-pressure acompanha a vazão real |
| Dashboard com valores ao vivo | Padrão | Tráfego principalmente QoS 0, que não é limitado |
Um valor de 1 garante processamento estritamente um por vez, o que também pode simplificar a lógica de ordenação, ao custo de vazão em links de alta latência. Cada vaga em trânsito exige uma ida e volta, então em um link celular com alta latência uma janela muito pequena pode limitar visivelmente a vazão. Meça com o seu tráfego real.
Receive Maximum e Ordem das Mensagens
Uma janela maior que 1 não quebra as garantias de ordem do MQTT: o broker continua enviando as mensagens de um topic na ordem em que as recebeu, e o cliente continua recebendo nessa ordem. O que muda é quantas podem estar aguardando confirmação ao mesmo tempo. Se a sua aplicação confirma mensagens fora de ordem, por exemplo ao entregá-las a um pool de threads, a ordem de processamento passa a ser responsabilidade sua, não do protocolo. O comportamento é o mesmo ao se conectar a um broker ConnectMQ, a um Mosquitto local ou a qualquer outro broker MQTT 5, porque a janela é negociada pelo próprio protocolo, e não configurada em um único lado.
Checklist de Solução de Problemas
- As mensagens chegam devagar após a reconexão. É a janela funcionando. Aumente-a se o dispositivo aguentar ou reduza o acúmulo com message expiry.
- O dispositivo ainda fica sem memória. Verifique se a maior parte do tráfego é QoS 0; o Receive Maximum não o limita. Confira também o maximum packet size.
- O publicador trava. A send quota do seu cliente chegou a zero porque o receive maximum do broker é pequeno ou os PUBACKs estão lentos. Registre as properties do CONNACK no log.
- Desconexão com 0x93. Um cliente enviou mais mensagens em trânsito do que o permitido; atualize ou corrija a biblioteca de cliente.
- A configuração não tem efeito. Você está se conectando com MQTT 3.1.1. O Receive Maximum só existe no MQTT 5.
Para entender como as mensagens enfileiradas se acumulam em primeiro lugar, veja como o MQTT enfileira mensagens para clientes offline. É novo no protocolo? Comece pelo tutorial de MQTT.
Ajuste o Controle de Fluxo na ConnectMQ
A ConnectMQ roda MQTT 3.1.1 e 5.0 sobre TCP, TLS, WebSocket e WebSocket seguro, então properties do MQTT 5 como o Receive Maximum funcionam a partir de qualquer cliente. Cotas por plano e proteção contra flood mantêm estável um broker MQTT na nuvem compartilhado, enquanto o Receive Maximum protege cada um dos seus dispositivos. Crie um broker Serverless gratuito, sem cartão de crédito, conecte-se com YOUR_USERNAME, YOUR_PASSWORD e YOUR_CLIENT_ID e use o cliente no navegador do portal para disparar rajadas de teste contra os seus dispositivos. Para frotas em produção, um broker MQTT gerenciado elimina totalmente o trabalho de operar o broker.
Perguntas frequentes
Qual é o receive maximum MQTT padrão?
Se a property não for enviada, o receive maximum é 65.535, o que na prática significa nenhum limite. Os clientes precisam definir explicitamente um valor menor para ter controle de fluxo.
O receive maximum limita mensagens QoS 0?
Não. Ele só conta pacotes PUBLISH QoS 1 e QoS 2 que ainda não foram confirmados. Mensagens QoS 0 nunca ficam em trânsito nesse sentido.
O receive maximum está disponível no MQTT 3.1.1?
Não. É uma property do MQTT 5. Clientes MQTT 3.1.1 dependem de configurações de in-flight específicas de cada biblioteca, que não são comunicadas ao broker.
O que significa o reason code 0x93?
Significa Receive Maximum exceeded: o remetente tinha mais mensagens QoS 1 ou 2 não confirmadas em trânsito do que o receptor permitia, então o receptor fechou a conexão.



