Testes de brokers

Teste de estresse de conexões MQTT: quantos clientes um broker aguenta?

Faça um teste de estresse de conexões MQTT: abra dezenas de milhares de clientes, ajuste ulimit e portas efêmeras, use vários IPs de origem e simule reconexões.

Atualizado · 9 min de leitura

Teste de estresse de conexões MQTT: quantos clientes um broker aguenta?

Um teste de estresse de conexões MQTT responde a uma pergunta simples: quantos clientes este broker consegue manter ao mesmo tempo e com que rapidez consegue aceitá-los? O método é aumentar conexões ociosas a uma taxa controlada enquanto você observa memória, CPU e latência de conexão do broker — e garantir que o limite atingido pertence ao broker, não à máquina que gera a carga.

A maioria das primeiras tentativas para em um número suspeitosamente redondo. Este guia mostra como remover esses limites do lado do cliente, rodar o teste com emqtt-bench ou com um script Node.js e simular a tempestade de reconexões que vem depois de cada reinício do broker.

O que você está medindo de fato

MétricaPor que importa
Máximo de conexões simultâneasTamanho de frota que o broker consegue manter ociosa
Taxa de conexão (CONNECT/s)Com que rapidez uma frota volta depois de uma queda
Latência de conexão (p50/p99)Tempo do SYN do TCP até o CONNACK sob carga
Memória do broker por conexãoPlanejamento de capacidade; meça, não chute
Tipos de erroRecusas, timeouts, reason codes do CONNACK

Conexões custam ao broker memória para buffers de socket, estado de sessão e inscrições, além de CPU para handshakes TLS e processamento de keep-alive. Tudo isso depende do broker, da versão e da configuração, então o único número que importa é o que você mede com as suas configurações: mesmo TLS, mesmo keep-alive, mesma autenticação e aproximadamente as mesmas inscrições por cliente.

Corrija o gerador de carga primeiro

Uma única máquina Linux tem três limites que travam as conexões de saída muito antes de um broker decente desistir.

Descritores de arquivo

Cada socket TCP é um descritor de arquivo. O limite soft padrão costuma ser 1.024:

Verificar e aumentar o limite de arquivos abertos
ulimit -n                 # limite soft atual
ulimit -n 200000          # aumentar neste shell (até o limite hard)
cat /proc/sys/fs/file-max # teto do sistema

Para uma mudança permanente, defina nofile em /etc/security/limits.conf ou, no Docker, passe --ulimit nofile=200000:200000.

Portas efêmeras

Cada conexão de um IP de origem para o mesmo IP e porta do broker precisa de uma porta local única. A faixa padrão do Linux é 32768–60999, cerca de 28.000 portas — o lugar clássico onde os testes empacam.

Ampliar a faixa de portas locais
sysctl net.ipv4.ip_local_port_range
sudo sysctl -w net.ipv4.ip_local_port_range="1024 65000"

Isso dá cerca de 64.000 conexões por IP de origem. Além disso, você precisa de mais IPs de origem (ou mais portas/IPs no broker, ou mais máquinas de carga).

Rastreamento de conexões

Se o gerador de carga roda um firewall com rastreamento de conexões (comum com Docker e iptables), a tabela do conntrack pode encher. O log do kernel então mostra nf_conntrack: table full, dropping packet.

Inspecionar e aumentar os limites do conntrack
sudo sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
sudo sysctl -w net.netfilter.nf_conntrack_max=262144

Rodando o teste com emqtt-bench

O emqtt-bench foi feito para isso. O comando conn abre conexões a uma taxa fixa e as mantém vivas:

10.000 conexões a 500 por segundo
docker run --rm -it --ulimit nofile=200000:200000 emqx/emqtt-bench conn \
  -h "$MQTT_HOST" -p 1883 \
  -c 10000 -R 500 -k 60 \
  -u "$MQTT_USER" -P "$MQTT_PASS"

Defina MQTT_HOST como o seu próprio broker; o endpoint compartilhado broker.connectmq.com serve só para pequenos smoke tests. -c é o número de clientes, -R a taxa de conexão por segundo e -k o keep-alive em segundos. Use o mesmo keep-alive dos seus dispositivos: keep-alives mais curtos significam mais tráfego PINGREQ por cliente ocioso.

Para ir além de um IP de origem, adicione endereços secundários à máquina de carga e deixe o emqtt-bench distribuir os clientes entre eles com --ifaddr. Use a rede do host para que o contêiner enxergue esses endereços:

50.000 conexões a partir de dois IPs de origem
docker run --rm -it --network host --ulimit nofile=200000:200000 \
  emqx/emqtt-bench conn \
  -h "$MQTT_HOST" -p 1883 \
  -c 50000 -R 1000 -k 60 \
  --ifaddr 10.0.0.11,10.0.0.12

Suba em degraus — por exemplo 10k, 25k, 50k — e mantenha cada nível por vários minutos. Um broker que aceita 50.000 conexões mas começa a derrubá-las no próximo ciclo de keep-alive não passou no teste.

Um script Node.js que você pode modificar

Quando você precisa de fluxos de autenticação reais, inscrições por cliente ou client IDs personalizados, um pequeno script com MQTT.js 5 é mais fácil de adaptar do que uma ferramenta de benchmark:

conn-stress.mjs
import mqtt from "mqtt";

const URL = process.env.MQTT_URL ?? "mqtt://broker.connectmq.com:1883";
const TOTAL = Number(process.argv[2] ?? 100); // padrão: um pequeno smoke test
const RATE = Number(process.argv[3] ?? 200); // novas conexões por segundo
let opened = 0, up = 0, errors = 0, closes = 0;

function open(i) {
  const c = mqtt.connect(URL, {
    clientId: `stress-${process.pid}-${i}`,
    username: process.env.MQTT_USER,
    password: process.env.MQTT_PASS,
    keepalive: 60,
    connectTimeout: 15_000,
    reconnectPeriod: 2000 + Math.floor(Math.random() * 8000), // jitter por cliente
  });
  let online = false;
  c.on("connect", () => {
    if (!online) { online = true; up++; }
    c.subscribe(`testmqtt/stress/${i}/cmd`, { qos: 1 });
  });
  c.on("close", () => { if (online) { online = false; up--; } closes++; });
  c.on("error", () => errors++);
}

const timer = setInterval(() => {
  for (let k = 0; k < RATE / 10 && opened < TOTAL; k++) open(opened++);
  if (opened >= TOTAL) clearInterval(timer);
}, 100);

setInterval(() => {
  console.log(`${new Date().toISOString()} opened=${opened} connected=${up} closes=${closes} errors=${errors}`);
}, 1000);
Rodar com limite de arquivos maior
npm install mqtt@5
ulimit -n 100000
MQTT_URL="mqtts://$MQTT_HOST:8883" node conn-stress.mjs 20000 300

Um processo Node.js mantém tranquilamente alguns milhares de conexões ociosas; para mais, rode vários processos ou máquinas. Observe a CPU do gerador de carga — se estiver no máximo, seus números medem o cliente, não o broker. Para entender a resposta do broker a conexões recusadas, consulte o código do CONNACK na referência de reason codes.

Observe o lado do broker

Os contadores do cliente contam só metade da história. Em cada degrau, registre no host do broker:

Host do broker
ss -s                                            # resumo de sockets
ss -tn state established '( sport = :1883 )' | wc -l
ps -o rss=,pcpu= -p "$(pgrep -d, mosquitto)"      # memória (KB) e CPU do processo do broker

O Mosquitto também publica $SYS/broker/clients/connected; inscreva-se com o cliente online ou com mosquitto_sub -t '$SYS/broker/clients/connected' -v — veja tópicos $SYS do MQTT. Lembre-se de que o host do broker precisa da mesma folga de descritores de arquivo: para o Mosquitto com systemd, defina LimitNOFILE= em um drop-in do serviço e confira max_connections no mosquitto.conf (o gerador de configuração do Mosquitto cria uma base). Erros que aparecem só nos logs do broker são tratados em conexões demais.

A memória por conexão é fácil de calcular: (RSS com N conexões − RSS ocioso) ÷ N. Meça com e sem TLS e com o seu número real de inscrições, porque ambos mudam o resultado.

Simule uma tempestade de reconexões

Manter 50.000 clientes ociosos é a parte fácil. O momento perigoso é quando todos reconectam ao mesmo tempo — depois de um reinício do broker, de um failover do load balancer ou de uma oscilação de rede regional. É um teste simultâneo de taxa de conexão, capacidade de handshakes TLS e capacidade do backend de autenticação.

Duas formas de provocá-la no seu próprio broker:

  1. Reinicie o broker enquanto o script mantém as conexões e observe connected= voltar a subir.
  2. Corte a rede entre o gerador de carga e o broker por mais tempo que o keep-alive (por exemplo, com uma regra de iptables ou com o Toxiproxy) e depois restaure.

Registre quanto tempo leva para voltar ao total de conexões e quantas tentativas falharam no caminho. Com um intervalo de reconexão fixo, todos os clientes tentam no mesmo instante e o broker recebe ondas sincronizadas. O reconnectPeriod com jitter do script acima as espalha; compare as duas variantes e você verá por que as tempestades de reconexão são um problema de design do cliente tanto quanto de capacidade do broker.

Checklist do teste de estresse de conexões

  1. Aumente ulimit -n, amplie ip_local_port_range e verifique o conntrack em cada máquina de carga.
  2. Planeje os IPs de origem: cerca de 64.000 conexões por IP de origem por endpoint do broker.
  3. Reproduza a produção: TLS, keep-alive, autenticação e inscrições por cliente.
  4. Suba em degraus e mantenha cada um por vários intervalos de keep-alive.
  5. Registre conexões, memória, CPU e latência de conexão do broker em cada degrau.
  6. Provoque uma tempestade de reconexões e meça o tempo de recuperação.
  7. Repita após upgrades ou mudanças de configuração do broker.

Um teste de estresse de conexões é principalmente uma questão de honestidade: remova os limites do seu lado, imite o comportamento real dos clientes e force até que o broker — e não o gerador de carga — mostre onde está o teto. Depois planeje sua frota com folga abaixo dele e garanta que seus clientes reconectem com jitter, para que esse teto nunca seja testado por acidente.

Perguntas frequentes

Por que meu teste de conexões MQTT para em cerca de 28.000 clientes?

Normalmente é o gerador de carga ficando sem portas efêmeras: por padrão, o Linux usa cerca de 28.000 portas locais para conexões de um IP de origem para o mesmo IP e porta do broker. Amplie net.ipv4.ip_local_port_range ou adicione mais IPs de origem.

Quantas conexões um broker MQTT aguenta?

Depende do broker, da memória, da CPU, do TLS, do keep-alive e do tráfego de mensagens, então não existe um número universal. Meça: aumente as conexões registrando memória e CPU do broker e encontre o ponto em que a latência de conexão ou os erros sobem.

Posso fazer teste de estresse em um broker MQTT público?

Não. Abrir milhares de conexões em um broker público compartilhado o degrada para todos e fará você ser limitado ou bloqueado. Faça testes de estresse de conexões apenas em brokers seus ou em instâncias destinadas a isso.