Testes de brokers

Teste de throughput MQTT: mensagens por segundo explicadas

Como fazer um teste de throughput MQTT: msg/s de entrada e saída, encontrar a taxa sustentável, detectar backpressure e converter msg/s em largura de banda.

Atualizado · 9 min de leitura

Teste de throughput MQTT: mensagens por segundo explicadas

"Mensagens por segundo" é o número de MQTT mais citado e o mais mal compreendido. Um broker pode aceitar 50.000 msg/s dos publishers e ainda assim entregar só metade, ou entregar todas com dois segundos de atraso. Um teste de throughput MQTT útil mede a taxa que o sistema consegue sustentar — entrando e saindo — enquanto a latência fica estável e nada se perde, com os seus próprios payloads e estrutura de topics.

Este guia define os números, mostra como encontrar a taxa sustentável passo a passo e explica os sinais de backpressure que avisam quando você passou do limite. Para a visão geral de conexões e latência, veja teste de carga de broker MQTT.

Entrada, saída e fan-out

TermoSignificadoOnde medir
Taxa de entrada (ingress)Pacotes PUBLISH por segundo que o broker aceita dos publishersLado do publisher (enviados / confirmados)
Taxa de saída (egress)Pacotes PUBLISH por segundo que o broker entrega aos subscribersLado do subscriber (recebidos)
Fator de fan-outNúmero médio de subscribers por mensagemSaída ÷ entrada
Taxa sustentávelMaior entrada em que a saída acompanha, a latência é estável e os erros ficam em zeroOs dois lados, por minutos

O fator de fan-out é o que as pessoas esquecem. Um publisher a 1.000 msg/s com 20 subscribers no topic gera 20.000 msg/s de saída. O trabalho do broker se aproxima mais da saída do que da entrada, então sempre informe as duas. Para os casos extremos, veja o teste de fan-out e o teste de fan-in.

Throughput de pico vs sustentável

Todo sistema consegue absorver por um tempo uma rajada acima da capacidade real: buffers de socket, filas do cliente e do broker se enchem e escondem o problema. Um teste de cinco segundos mede esses buffers, não o broker. A taxa sustentável só aparece quando você mantém cada nível de carga por tempo suficiente para as filas atingirem um estado estável — normalmente alguns minutos.

O método é uma rampa em degraus:

  1. Comece com uma taxa que você tem certeza que o sistema aguenta (por exemplo, 20 % do pico esperado).
  2. Mantenha por 2–5 minutos e registre entrada, saída, latência p99 e erros.
  3. Aumente em um passo fixo (por exemplo +25 %) e mantenha de novo.
  4. Pare quando a saída deixar de acompanhar a entrada, a latência p99 continuar subindo dentro de um degrau ou surgirem erros.
  5. O último degrau limpo é a sua taxa sustentável. Planeje a produção com uma margem confortável abaixo dele.

No gráfico de taxa oferecida vs latência, você vê uma linha plana seguida de um "joelho" onde a latência se curva para cima. O joelho é o número que você procura — não a taxa máxima que o gerador de carga conseguiu empurrar.

Rodando a rampa com emqtt-bench

O emqtt-bench gera taxas altas a partir de uma só máquina. Inicie primeiro os subscribers e depois os publishers em taxas crescentes. O intervalo de publish -I é por cliente, em milissegundos, então a taxa total é clientes × 1000 / I:

Subscribers — um por topic de publisher
docker run --rm -it emqx/emqtt-bench sub \
  -h broker.connectmq.com -p 1883 \
  -c 50 -t 'testmqtt/tput/%i' -q 1 \
  -u "$MQTT_USER" -P "$MQTT_PASS"
Publishers — degrau 1: 50 clientes × 20 msg/s = 1.000 msg/s
docker run --rm -it emqx/emqtt-bench pub \
  -h broker.connectmq.com -p 1883 \
  -c 50 -I 50 -t 'testmqtt/tput/%i' -s 256 -q 1 \
  -u "$MQTT_USER" -P "$MQTT_PASS"

Nos degraus seguintes, reduza -I (50 → 40 → 30 → 20 → 10 ms) ou adicione clientes. O emqtt-bench imprime as taxas enviadas e recebidas a cada segundo; copie-as para uma planilha por degrau. Como aqui cada publisher tem seu próprio subscriber, o fator de fan-out é 1 e a saída deve ser igual à entrada.

Uma sonda em Python para lacunas e atraso

Contadores de taxa escondem duas coisas: quais mensagens sumiram e quanto as outras atrasaram. Rode esta pequena sonda com paho-mqtt 2.x ao lado do benchmark. Ela publica mensagens numeradas e com timestamp a uma taxa modesta e informa lacunas e atraso a cada segundo; o atraso da sonda subindo enquanto o bench roda é o seu sinal de backpressure:

tput_probe.py
import json, time, uuid
import paho.mqtt.client as mqtt

HOST, PORT = "broker.connectmq.com", 1883
TOPIC = f"testmqtt/tput/probe-{uuid.uuid4().hex[:6]}"
RATE = 100                      # mensagens de sonda por segundo
stats = {"n": 0, "last": -1, "gaps": 0, "lag": []}

def on_message(client, userdata, msg):
    m = json.loads(msg.payload)
    if m["i"] > stats["last"] + 1:
        stats["gaps"] += m["i"] - stats["last"] - 1
    stats["last"] = max(stats["last"], m["i"])
    stats["n"] += 1
    stats["lag"].append((time.perf_counter() - m["t"]) * 1000)

def client(name):
    c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"{name}-{uuid.uuid4().hex[:6]}")
    c.connect(HOST, PORT)
    c.loop_start()
    return c

sub = client("probe-sub")
sub.on_message = on_message
sub.subscribe(TOPIC, qos=1)
pub = client("probe-pub")
time.sleep(1)

i, next_report = 0, time.time() + 1
while True:
    pub.publish(TOPIC, json.dumps({"i": i, "t": time.perf_counter()}), qos=1)
    i += 1
    time.sleep(1 / RATE)
    if time.time() >= next_report:
        lag = sorted(stats["lag"]) or [0]
        print(f"rx {stats['n']:>5}/s  gaps {stats['gaps']}  "
              f"lag p50 {lag[len(lag)//2]:.1f} ms  p99 {lag[int(len(lag)*0.99)-1]:.1f} ms")
        stats.update(n=0, lag=[])
        next_report += 1

Um único processo Python não vai gerar dezenas de milhares de mensagens por segundo — isso é trabalho da ferramenta de benchmark. A sonda é um termômetro, não um aquecedor.

Reconhecendo backpressure

O MQTT não tem um sinal global de controle de fluxo; o backpressure aparece como filas crescendo em algum lugar. Saiba onde olhar:

SinalOnde ficaO que significa
A taxa enviada do publisher cai abaixo da metaJanela inflight do cliente cheia (QoS 1/2)Os PUBACKs voltam mais devagar do que você publica — o broker ou o caminho estão saturados
O atraso da sonda sobe sem parar durante um degrauFila do broker ou do subscriberA saída não acompanha; você está acima da taxa sustentável
Lacunas na sonda em QoS 0Broker descartando mensagens para clientes lentosLimite de fila atingido; o broker está descartando carga
Subscribers desconectados com erros de fila ou cotaLimites por cliente do brokerVeja fila de mensagens cheia e consumidores lentos
CPU do gerador de carga em 100 %Sua máquina de testeO resultado mede o cliente, não o broker

Em QoS 1 e 2, a janela inflight é o limitador principal: um cliente só pode ter um certo número de mensagens não confirmadas (max_inflight_messages_set() no paho, Receive Maximum no MQTT 5). Com 20 mensagens em voo e um round trip de 50 ms, uma conexão fica perto de 400 msg/s, por mais rápido que seja o broker. É por isso que testes em links distantes parecem piores do que na mesma região, e por isso mais conexões — e não um loop mais rápido — elevam o teto. Receive Maximum e controle de fluxo explica o mecanismo.

De msg/s para largura de banda

Throughput também é um orçamento de rede. Um PUBLISH MQTT custa aproximadamente: cabeçalho fixo (2–5 bytes) + tamanho do topic + 2 bytes + identificador de pacote (2 bytes com QoS > 0) + propriedades MQTT 5 + payload. Some os cabeçalhos TCP/IP e, se houver, o overhead dos registros TLS. QoS 1 adiciona um PUBACK de 4 bytes na direção oposta.

Exemplo: payload de 256 bytes, topic de 20 bytes, QoS 1 → cerca de 285 bytes por PUBLISH antes do TCP/IP. A 5.000 msg/s de saída, isso dá cerca de 1,4 MB/s, ou aproximadamente 11–12 Mbit/s de tráfego MQTT antes do overhead de IP e TLS. Em mensagens pequenas o overhead domina, então agrupar leituras em uma só mensagem costuma aumentar o throughput efetivo mais do que qualquer ajuste no broker. A calculadora de tamanho de pacote faz a conta para o seu topic e payload exatos.

Documentando o resultado

Um resultado de throughput só é reutilizável se informar as condições. Registre no mínimo: versão e configuração do broker, QoS, tamanho do payload, número de topics, fator de fan-out, número de conexões publisher e subscriber, TLS ligado ou não, duração de cada degrau e região do gerador de carga. Depois informe as taxas sustentáveis de entrada e saída com a latência p99, não o pico. Ao comparar execuções mais tarde, interpretando resultados de benchmark cobre as armadilhas comuns.

Antes da rampa, faça um smoke test dos topics com o cliente MQTT online para confirmar credenciais e ACLs — um erro de permissão sob carga parece exatamente um problema de throughput.

Um teste de throughput é a busca pelo joelho, não uma corrida pelo maior número. Suba em degraus, mantenha cada degrau por tempo suficiente, observe atraso e lacunas em vez de só contadores e informe a saída junto com a entrada — esse é o número com que você pode planejar capacidade.

Perguntas frequentes

Qual é um bom número de throughput MQTT?

Não existe um número universal: depende do tamanho do payload, QoS, fan-out, TLS, persistência e hardware. O número útil é a maior taxa que o seu broker sustenta com o seu próprio padrão de tráfego enquanto a latência fica estável e nada se perde.

Por que a taxa recebida é menor que a enviada?

Ou as mensagens estão em fila em algum lugar (janelas inflight, filas do broker, buffers do cliente) ou estão sendo descartadas. Se a diferença cresce com o tempo, você está acima da taxa sustentável. Em QoS 0, uma diferença crescente geralmente significa perda.

O QoS afeta o throughput MQTT?

Sim. QoS 1 adiciona um PUBACK por mensagem e por salto, e QoS 2 adiciona três pacotes extras; ambos são limitados pela janela inflight. Espere taxas de pico menores com QoS mais alto, principalmente em links de alta latência.