Test di carico

Prove di sforzo del broker su due banchi — via rete e in loopback — per separare il comportamento del broker stesso da quello dell'ambiente. Di seguito: metodo, tabelle complete e conclusioni brevi.

Scarica il rapporto (PDF)

I banchi

Data: 24 luglio 2026 · Protocollo: MQTT 3.1.1 · Strumento: client di carico interno cmd/loadtest costruito sul codec del progetto, senza librerie MQTT di terze parti.

Le prove sono state fatte su due banchi per separare il comportamento del broker stesso dall'influenza dell'ambiente: rete contro loopback, hardware modesto contro hardware potente.

Banco A — via reteBanco B — loopback
Processore del broker12 core2× Xeon E5-2690 v4 a 2,6 GHz (28 core / 56 thread)
Memoria del broker16 GB128 GB
Indirizzo192.168.1.84:1883 (LAN)127.0.0.1:1883 (loopback)
Generatore di caricoMacchina separata sulla LANLa stessa macchina del broker
Porte effimere del client16 384 (predefinito)55 000 (allargato)
Come leggere il confronto. I banchi non sono equivalenti e rispondono a domande diverse. Il banco A mostra il funzionamento su una rete con generatore dedicato. Il banco B elimina la rete (latenza quasi nulla) e dà al broker hardware potente, ma generatore e broker condividono gli stessi core. Perciò latenza e fan-out si confrontano direttamente, l'ingestione no: sul banco B il client ruba core al broker.

Sintesi

  • Latenza. Via rete, la latenza end-to-end «publisher → broker → subscriber» sotto carico normale si aggira sul 1 ms; in loopback è di 73 µs (~16 volte meno, tolta la rete).
  • Connessioni. Via rete il broker ha retto 10 000 sessioni senza errori (oltre, abbiamo urtato il limite di porte del client). Su hardware potente in loopback: 30 000 senza errori e ~41 000 in un tentativo da 50 000 (il limite di una sola macchina, non del broker).
  • Ingestione. Il picco via rete è stato di ≈277 000 messaggi/s; in loopback l'ingestione è più bassa (≈185 000/s) perché il generatore contende i core al broker.
  • Fan-out (instradamento). Su hardware potente il broker ha consegnato ≈1 350 000 messaggi/s agli iscritti (fan-out ×100) contro ≈22 000/s via rete.
  • Robustezza. Su nessuno dei due banchi il broker si è bloccato o è andato in crash. In sovraccarico i messaggi QoS 0 diretti a iscritti in ritardo vengono scartati in modo ordinato; il log del broker non contiene un solo errore in tutte le prove.

Capacità di connessioni simultanee

Ogni connessione è un ciclo completo CONNECT → CONNACK con autenticazione; poi la sessione viene tenuta aperta (verificata con PINGREQ).

Banco A — via rete

ObiettivoStabiliteFalliteInstaurazione p50p99
10100146 ms162 ms
1001000702 ms1,12 s
1000100004,21 s9,78 s
5000500004,64 s8,59 s
10 00010 00004,92 s10,52 s
15 00014 8791217,43 s14,09 s

Banco B — loopback (salita graduale)

ObiettivoStabiliteFalliteInstaurazione p50p99
100010000886 ms1,06 s
10 00010 0000903 ms1,55 s
30 00030 0000923 ms1,90 s
50 00041 2190984 ms1,90 s

Analisi

  • Via rete, fino a 10 000 senza un solo errore; i 121 fallimenti a 15 000 sono l'esaurimento delle porte effimere del client (16 384). È il limite del generatore, non del broker.
  • In loopback, dopo aver allargato l'intervallo di porte, il broker ha retto 30 000 sessioni senza difetti; a 50 000 ne restavano vive circa 41 000 alla fine del periodo di mantenimento, perché entrambi i lati condividono una macchina. Il log del broker è rimasto pulito per tutto il tempo, senza errori.
  • Osservazione collaterale. In loopback una raffica di connessioni troppo brusca (1000 dial simultanei) provoca connection refused: la coda di accettazione trabocca, perché senza ritardo di rete tutti i SYN arrivano insieme. Una salita graduale (200 per volta) elimina del tutto l'effetto. Su una rete la latenza attenua la raffica da sola.

Portata in ingestione

Un gruppo di publisher pubblica il più velocemente possibile; si misura il ritmo di ingestione del broker.

QoS 0 · payload di 64 byte

PublisherBanco A (rete)Banco B (loopback)
10271 006/s185 012/s
100236 422/s101 457/s
500103 546/s125 324/s

L'ingestione è più alta via rete — non perché il broker del banco A sia più veloce, ma perché lì il generatore gira su una macchina separata. In loopback client e broker condividono gli stessi core, il che limita il ritmo complessivo.

QoS 1 (con conferma PUBACK) · 64 B

PublisherBanco A (rete)Banco B (loopback)
1010 292/s63 606/s
10022 085/s48 983/s

Il client di prova per QoS 1 è sincrono (aspetta un PUBACK dopo ogni messaggio), quindi è vincolato al tempo di andata e ritorno. In loopback quel tempo è quasi nullo, da cui il guadagno di circa 6 volte. Questo mostra con chiarezza che i numeri di QoS 1 sono dettati dalla latenza di rete, non dal broker.

Scansione delle dimensioni dei messaggi · QoS 0 · 50 publisher

DimensioneBanco A (rete)Banco B (loopback)
64 B277 198/s · 17,7 MB/s101 961/s · 6,5 MB/s
256 B124 997/s · 32,0 MB/s102 029/s · 26,1 MB/s
1024 B38 458/s · 39,4 MB/s102 151/s · 104,6 MB/s

Due tetti diversi:

  • Via rete si urta il collegamento verso ≈40 MB/s: quando il messaggio cresce, i msg/s calano mentre i MB/s restano vicini al limite.
  • In loopback il collegamento è enorme, così il numero di messaggi resta stabile (~102 000/s con 50 connessioni) e il volume sale a 104,6 MB/s. Qui il collo di bottiglia è l'elaborazione per messaggio (CPU), non i byte.

Consegna end-to-end

Nel corpo del messaggio viene inserito un marcatore temporale; si misurano la consegna reale e la latenza end-to-end. Fan-out significa consegna a ogni iscritto il cui filtro corrisponde.

Banco A — via rete

ScenarioPubblicazioneConsegnamedia / p50 / p99
10 sub / 10 pub / 100 msg/s — normale986/s9863/s1,17 / 1,0 / 3,6 ms
100 sub / 10 pub / senza freno261 551/s22 351/s780 / 453 / 678 ms
100 sub / 10 pub / 500 msg/s4934/s12 150/s1,87 / 1,77 / 2,83 s
1000 sub / 5 pub / 100 msg/s494/s178 961/s2,29 s / 480 / 952 ms

Banco B — loopback

ScenarioPubblicazioneConsegnamedia / p50 / p99
10 sub / 10 pub / 100 msg/s — normale987/s9867/s73 / ~0 / 555 µs
100 sub / 10 pub / senza freno47 557/s1 353 431/s514 / 135 / 251 ms
100 sub / 10 pub / 500 msg/s3608/s360 834/s17,1 / 10,5 / 71,6 ms
1000 sub / 5 pub / 100 msg/s494/s183 746/s4,73 / 4,54 / 15,1 ms

Analisi

  • Latenza sotto carico normale: 1,17 ms via rete contro 73 µs in loopback. La differenza è il tragitto di rete.
  • Instradamento sotto carico: la macchina potente ha consegnato ≈1,35 milioni di messaggi/s agli iscritti (fan-out ×100) — due ordini di grandezza sopra il banco di rete (22 mila/s), dove sia il broker sia il collegamento sono piccoli.
  • Negli scenari a fan-out elevato anche il generatore diventa il collo di bottiglia (tutti gli iscritti in un processo su una macchina), quindi i picchi di consegna sono un limite inferiore di ciò che il broker sa fare.

Confronto diretto

IndicatoreBanco A — rete (12 core)Banco B — loopback (28 core)
Connessioni senza errori10 00030 000
Picco di connessioni14 879 (limite del client)~41 000 (limite di una macchina)
Ingestione QoS 0 (64 B), picco271 006/s185 012/s
Ingestione QoS 1 (64 B), picco22 085/s63 606/s
Tetto di volume≈40 MB/s (collegamento)≈105 MB/s (CPU)
Latenza (normale), mediana1,0 ms~0 (73 µs in media)
Consegna con fan-out, picco22 351/s1 353 431/s
Errori nel log del brokernessunonessuno

Riserve di metodo

  • Il ritmo di ingestione non è direttamente confrontabile tra i banchi: sul banco B generatore e broker condividono i core; sul banco A il generatore è una macchina separata.
  • Su ogni banco il carico proviene da una sola macchina client; in alcuni scenari (fan-out ampio, decine di migliaia di connessioni) il collo di bottiglia è quella macchina e non il broker.
  • Il client di prova per QoS 1 è sincrono — i numeri di QoS 1 sottostimano il potenziale del broker.
  • Le metriche di sistema del broker (CPU e memoria durante le prove) non sono state raccolte; lo stato del server si deduce dal suo log (nessun errore) e dal comportamento lato client.

Riprodurre

Ogni prova parte da un unico binario loadtest; di seguito i comandi delle tre modalità.

go build -o loadtest ./cmd/loadtest

# Capacità di connessioni (salita graduale contro la coda di accettazione)
./loadtest -addr HOST:1883 -user myelx -pass *** \
    -mode conn -levels 1000,10000,30000,50000 -hold 3s -dial-concurrency 200

# Portata in ingestione
./loadtest -addr HOST:1883 -user myelx -pass *** \
    -mode pub -pubs 10 -qos 0 -payload 64 -duration 10s

# Consegna end-to-end e latenza
./loadtest -addr HOST:1883 -user myelx -pass *** \
    -mode pubsub -subs 10 -pubs 10 -qos 0 -rate 100 -payload 64 -duration 10s