試験環境
実施日:2026 年 7 月 24 日 · プロトコル:MQTT 3.1.1 · 道具:自作の負荷クライアント cmd/loadtest。本プロジェクトのコーデックを使い、サードパーティ製 MQTT ライブラリは用いていません。
ブローカー自身のふるまいと環境の影響を切り分けるため、2 つの環境で試験しました。ネットワーク経由とループバック、控えめな機材と強力な機材です。
| 環境 A — ネットワーク経由 | 環境 B — ループバック | |
|---|---|---|
| ブローカーの CPU | 12 コア | 2× Xeon E5-2690 v4 @ 2.6 GHz(28 コア / 56 スレッド) |
| ブローカーのメモリー | 16 GB | 128 GB |
| アドレス | 192.168.1.84:1883(LAN) | 127.0.0.1:1883(ループバック) |
| 負荷生成側 | LAN 上の別のマシン | ブローカーと同じマシン |
| クライアントの一時ポート | 16,384(既定) | 55,000(拡張) |
結果の要約
- 遅延。通常の負荷における「発行者 → ブローカー → 購読者」の端から端までの遅延は、ネットワーク経由でおよそ 1 ms、ループバックでは 73 µs(ネットワークを除いた分、約 16 分の 1)。
- 接続数。ネットワーク経由では 10,000 セッションを失敗なく維持しました(それ以上はクライアント側のポート上限に当たりました)。強力な機材でのループバックでは 30,000 を失敗なく、50,000 を狙った試行では 約 41,000(ブローカーではなく 1 台のマシンの限界)。
- 受信量。ネットワーク経由の最大は 毎秒約 277,000 メッセージ。ループバックではこれが下がります(毎秒約 185,000)。生成機がブローカーとコアを取り合うためです。
- ファンアウト(配送)。強力な機材では、購読者へ 毎秒約 1,350,000 メッセージ(ファンアウト ×100)を配りました。ネットワーク経由の毎秒約 22,000 と比べての値です。
- 安定性。どちらの環境でもブローカーは落ちも固まりもしませんでした。過負荷時には、遅れている購読者向けの QoS 0 のメッセージが行儀よく捨てられます。全試行を通じて、ブローカーのログにエラーは 1 件もありません。
同時接続の収容力
各接続は認証を伴う CONNECT → CONNACK の完全な流れで、その後セッションを開いたまま保ちます(PINGREQ で確認)。
環境 A — ネットワーク経由
| 目標 | 確立 | 失敗 | 確立 p50 | p99 |
|---|---|---|---|---|
| 10 | 10 | 0 | 146 ms | 162 ms |
| 100 | 100 | 0 | 702 ms | 1.12 s |
| 1,000 | 1,000 | 0 | 4.21 s | 9.78 s |
| 5,000 | 5,000 | 0 | 4.64 s | 8.59 s |
| 10,000 | 10,000 | 0 | 4.92 s | 10.52 s |
| 15,000 | 14,879 | 121 | 7.43 s | 14.09 s |
環境 B — ループバック(段階的に増やす)
| 目標 | 確立 | 失敗 | 確立 p50 | p99 |
|---|---|---|---|---|
| 1,000 | 1,000 | 0 | 886 ms | 1.06 s |
| 10,000 | 10,000 | 0 | 903 ms | 1.55 s |
| 30,000 | 30,000 | 0 | 923 ms | 1.90 s |
| 50,000 | 41,219 | 0 | 984 ms | 1.90 s |
考察
- ネットワーク経由では 10,000 まで失敗ゼロ。15,000 での 121 件の失敗はクライアント側の一時ポート(16,384)の枯渇です。ブローカーではなく生成機の限界です。
- ループバックでは、ポートの範囲を広げたあと 30,000 セッションを問題なく維持しました。50,000 では保持期間の終わりに約 41,000 が生きていました。両側が 1 台のマシンを分け合っているためです。ブローカーのログは終始きれいで、エラーはありません。
- 副次的な発見。ループバックでは接続の立ち上がりが急すぎると(同時に 1,000 の
dial)connection refusedが起きます。受け入れ待ち行列があふれるためで、ネットワークの遅延がないと SYN が一斉に届くからです。段階的に増やせば(1 回に 200)この現象は完全に消えます。ネットワーク越しなら遅延が自然に山をならします。
受信スループット
発行者の集団ができるだけ速く発行し、ブローカーの受信速度を測ります。
QoS 0 · 64 バイトのペイロード
| 発行者 | 環境 A(ネットワーク) | 環境 B(ループバック) |
|---|---|---|
| 10 | 271,006/s | 185,012/s |
| 100 | 236,422/s | 101,457/s |
| 500 | 103,546/s | 125,324/s |
受信量はネットワーク経由のほうが多いのですが、環境 A のブローカーが速いからではなく、そこでは生成機が別のマシンで動いているからです。ループバックではクライアントとブローカーが同じコアを分け合うので、合計の速度に上限がかかります。
QoS 1(PUBACK による確認あり)· 64 B
| 発行者 | 環境 A(ネットワーク) | 環境 B(ループバック) |
|---|---|---|
| 10 | 10,292/s | 63,606/s |
| 100 | 22,085/s | 48,983/s |
QoS 1 の試験クライアントは同期式で(1 通ごとに PUBACK を待つ)、往復時間に縛られます。ループバックでは往復がほぼ 0 なので、約 6 倍になりました。QoS 1 の数値がブローカーではなくネットワークの遅延で決まることが、はっきり分かります。
メッセージ長を変えた試験 · QoS 0 · 発行者 50
| 長さ | 環境 A(ネットワーク) | 環境 B(ループバック) |
|---|---|---|
| 64 B | 277,198/s · 17.7 MB/s | 101,961/s · 6.5 MB/s |
| 256 B | 124,997/s · 32.0 MB/s | 102,029/s · 26.1 MB/s |
| 1,024 B | 38,458/s · 39.4 MB/s | 102,151/s · 104.6 MB/s |
2 つの異なる天井が見えます:
- ネットワーク経由では毎秒約 40 MB で回線に当たります。メッセージが大きくなるほど毎秒の件数は落ち、MB/s は上限の近くに留まります。
- ループバックでは回線が桁違いに太いので件数は保たれ(接続 50 で毎秒約 102,000)、量は毎秒 104.6 MB まで伸びます。ここでの制約はバイト数ではなく、1 通あたりの処理(CPU)です。
端から端までの配信
メッセージ本体に時刻を埋め込み、実際の配信量と端から端までの遅延を測ります。ファンアウトとは、フィルターが一致する購読者すべてへの配信のことです。
環境 A — ネットワーク経由
| 条件 | 発行 | 配信 | 平均 / p50 / p99 |
|---|---|---|---|
| 購読 10 / 発行 10 / 毎秒 100 — 通常 | 986/s | 9,863/s | 1.17 / 1.0 / 3.6 ms |
| 購読 100 / 発行 10 / 制限なし | 261,551/s | 22,351/s | 780 / 453 / 678 ms |
| 購読 100 / 発行 10 / 毎秒 500 | 4,934/s | 12,150/s | 1.87 / 1.77 / 2.83 s |
| 購読 1,000 / 発行 5 / 毎秒 100 | 494/s | 178,961/s | 2.29 s / 480 / 952 ms |
環境 B — ループバック
| 条件 | 発行 | 配信 | 平均 / p50 / p99 |
|---|---|---|---|
| 購読 10 / 発行 10 / 毎秒 100 — 通常 | 987/s | 9,867/s | 73 / ~0 / 555 µs |
| 購読 100 / 発行 10 / 制限なし | 47,557/s | 1,353,431/s | 514 / 135 / 251 ms |
| 購読 100 / 発行 10 / 毎秒 500 | 3,608/s | 360,834/s | 17.1 / 10.5 / 71.6 ms |
| 購読 1,000 / 発行 5 / 毎秒 100 | 494/s | 183,746/s | 4.73 / 4.54 / 15.1 ms |
考察
- 通常の負荷での遅延は、ネットワーク経由で 1.17 ms、ループバックで 73 µs。差はネットワークの経路そのものです。
- 負荷時の配送:強力なマシンは購読者へ毎秒約 135 万メッセージ(ファンアウト ×100)を届けました。ブローカーも回線も小さいネットワーク環境(毎秒 2.2 万)に比べ、2 桁上です。
- ファンアウトが大きい条件では生成機も制約になります(すべての購読者が 1 台のマシンの 1 プロセスにいるため)。したがって配信量の最大値は、ブローカーの実力の下限とみるべきです。
両環境の比較
| 指標 | 環境 A — ネットワーク(12 コア) | 環境 B — ループバック(28 コア) |
|---|---|---|
| 失敗なしの接続数 | 10,000 | 30,000 |
| 接続数の最大 | 14,879(クライアントの限界) | 約 41,000(1 台のマシンの限界) |
| 受信 QoS 0(64 B)最大 | 271,006/s | 185,012/s |
| 受信 QoS 1(64 B)最大 | 22,085/s | 63,606/s |
| 量の上限 | 毎秒約 40 MB(回線) | 毎秒約 105 MB(CPU) |
| 遅延(通常)中央値 | 1.0 ms | ほぼ 0(平均 73 µs) |
| ファンアウト時の配信、最大 | 22,351/s | 1,353,431/s |
| ブローカーのログのエラー | なし | なし |
手法上の注意
- 受信速度は 2 つの環境で直接は比べられません。環境 B では生成機とブローカーがコアを分け合い、環境 A では生成機が別のマシンです。
- どちらの環境でも負荷は 1 台のクライアントマシンから与えています。条件によっては(大きなファンアウト、数万の接続)、制約になるのはブローカーではなくそのマシンです。
- QoS 1 の試験クライアントは同期式のため、QoS 1 の数値はブローカーの実力を控えめに示しています。
- 試験中のブローカー側のシステム指標(CPU・メモリー)は採取していません。サーバーの状態は、そのログ(エラーなし)とクライアント側のふるまいから推し量っています。
再現するには
どの試行も loadtest という 1 つのバイナリから起動します。以下は 3 つのモードのコマンドです。
go build -o loadtest ./cmd/loadtest
# 接続の収容力(段階的な立ち上げと受け入れ待ち行列)
./loadtest -addr HOST:1883 -user myelx -pass *** \
-mode conn -levels 1000,10000,30000,50000 -hold 3s -dial-concurrency 200
# 受信スループット
./loadtest -addr HOST:1883 -user myelx -pass *** \
-mode pub -pubs 10 -qos 0 -payload 64 -duration 10s
# 端から端までの配信と遅延
./loadtest -addr HOST:1883 -user myelx -pass *** \
-mode pubsub -subs 10 -pubs 10 -qos 0 -rate 100 -payload 64 -duration 10s