ファイナンス、情報通信技術のスキル・アグリゲーション・サイト

' . iseeit.jp 情報通信技術 . '
 
Message Queue / RabbitMQ ①

なぜWebアプリに
メッセージキューが必要か

このサイトの住宅ローン計算ツールは、裏側で計算サーバーと通信しています。 なぜ単純な一発リクエストではなく、わざわざ「順番待ちの箱」を挟むのか。 非力なVPSで重い処理を捌くための発想を、ブラウザ内シミュレーターで手を動かしながら確かめます。

RabbitMQ WebSocket 2GB VPS 体感デモ付き

§ 0 — 導入

目の前に、実在するツールがある

このサイトには 住宅ローン計算ツール があります。借入額・金利・返済期間を入れると、毎月の返済額や返済表を計算して返してくれる、ごく普通のツールに見えます。

ですがこのツール、実は入力を送るたびに fetch() でページを取りに行っているわけではありません。 裏側では WebSocket接続を張り、 さらにその奥でメッセージキューを経由して計算しています。

返済表の計算くらい、その場ですぐ返せばいいのでは? —— この記事は、その素朴な疑問に答えるところから始めます。答えは、このツールを動かしている ConoHa の 2GB RAM / 3コアという小さなサーバーの事情に隠れています。

§ 1 — 直接処理の限界

素朴に書くと、どこで壊れるか

いちばん素直な実装は「リクエストが来たら、その場で計算して返す」です。平常時はこれで何の問題もありません。 問題が牙をむくのは、次の3つの条件が同時に揃ったときだけです。

壊れる3条件

① 処理が重い — LLMの推論、大規模モンテカルロ、長期ローンの返済表生成。 1件でCPUとメモリをがっつり使う処理。
② 同時アクセスが起こりうる — 公開ツールである以上、複数人が同じ瞬間に「計算」を押す可能性は常にある。
③ サーバーが非力 — ここが本丸。潤沢なクラウドではなく、2GB RAM の小さなVPSで動かしている。

本当のボトルネックはCPUではなくメモリ。 重い処理が同時に何本も立ち上がると、まずRAMが先に枯渇します。すると Linux はスワップ(ディスクを仮想メモリとして使う)を始め、 ここから処理速度が桁で落ちます。いわゆるスワップ地獄。最悪、OOM Killer がプロセスを強制終了します。

つまり「同時に3人がノックしてきたら、3つの重い処理が一斉に立ち上がってメモリを食い尽くす」。 これが直接処理の限界です。下のシミュレーターで、まさにこの瞬間を再現してみます。

§ 2 — キューという発想

並べて、順番に

解決策はシンプルです。リクエストを直接処理係に渡さず、いったん「受付箱」に入れる。 処理係は、その箱から1つずつ取り出して片付ける。郵便受けとよく似ています。

こうすると、たとえ同じ瞬間に10件が殺到しても、実際に走る処理は常に1つ。 メモリを食い潰す同時起動が起きません。ピークがならされて、サーバーは常に「1件分」の負荷で回り続けます。

この受付箱を専門に担うミドルウェアが RabbitMQ です。登場人物は3人だけ。

RabbitMQ の登場人物

Producer(入れる人)— リクエストをキューに投げ込む。ここでは WebSocket サーバー。
Queue(箱)— リクエストが順番待ちで並ぶ場所。
Consumer(取り出す人)— 箱から1件取り出し、重い処理を実行して結果を返す。

§ 3 — 体感シミュレーター

直接処理 vs キュー経由

理屈より、動かすのが早い。左右でまったく同じリクエストを、直接処理とキュー経由でさばきます。 「同時リクエスト数」と「1処理の重さ」を上げていくと、どこで直接処理が崩壊するかが見えます。

Interactive · Front-end simulation
直接処理 vs キュー経由 体感シミュレーター
サーバー通信は一切なし。ブラウザ内で 2GB VPS のメモリ挙動を再現しています。数値は傾向を示す目安です。
同時リクエスト数 3 件
同じ瞬間に「計算」を押す人数
1処理の重さ(メモリ footprint) 中 · 40%
1件がRAMをどれだけ使うか。60%で2件同時に赤信号
直接処理 全件を同時起動
メモリ使用率0%
⚠ SWAP — スワップ発生・全処理が失速
経過
0.0s
最大メモリ
0%
状態
待機
キュー経由 prefetch=1 で直列
メモリ使用率0%
 
経過
0.0s
最大メモリ
0%
状態
待機
スライダーを調整して「リクエストを同時に投げる」を押してください。まずは重さ60%・6件で崩壊を見るのがおすすめです。

軽い処理・少人数なら、直接処理(並列)のほうが速く終わります。当然です。 ですが重さと同時数を上げていくと、直接処理のメモリメーターが限界線を越え、 スワップに落ちて全処理が一斉に失速します。総処理時間は逆転し、キュー経由のほうが速く、しかも安定します。

これが直感に反するポイントです。 「1件ずつ順番に処理したら遅いはず」——ところが非力なサーバーでは、 同時に走らせて共倒れするより、並べて確実に1つずつ返すほうが、結果的に速い
§ 4 — 設計判断

prefetch_count=1 という選択

シミュレーターのキュー側は「一度に1件だけ」処理していました。RabbitMQ でこれを指示するのが prefetch_count=1 の一行です。Consumer が「未処理の1件を片付けるまで、次を受け取らない」という設定です。

# consumer 側:一度に1メッセージだけ受け取る
await channel.set_qos(prefetch_count=1)

「並列数を増やせばもっと速いのでは?」——2GB VPS では逆です。並列数を増やすほど、 §3で見たメモリ枯渇に近づきます。詰まって全部落ちるより、順番に確実に返すほうが速い。 これは体感の総処理時間の話であり、シミュレーターがまさに示していたことです。

次回への伏線。 実は、この「箱」は1つではありません。AIコンシェルジュでは request / reply / cancel の3つのキューを使い分けています。なぜ分けたのか、特に 「途中で止める」をなぜ独立した箱にしたのか——次回の主題です。
§ 5 — 実例

同じ発想が、計算にもAIにも効く

この「並べて順番に」というパターンは、まったく毛色の違う2つの場面で、同じように効いています。

iseeit.jp の住宅ローン計算 — WebSocket で計算サーバーに繋ぎ、返済表の生成をキュー経由で捌く。 金利変更ルールを含む長期返済のシミュレーションは、地味に重い処理です。
sasagawa.tokyo の AIコンシェルジュ — LLM の推論という、桁違いに重い処理をキューで直列化する。 CPU推論のLLMは1件でRAMを大きく占有するため、直列化は必須でした。

金融計算とAI推論という、一見無関係な2つが、 「非力なサーバーで、止めずに、確実に返す」という同じ制約から、同じ設計に行き着いた。 これが Finance × ICT というこのサイトの一つの縮図です。

この記事のまとめ

  • 重い・同時・非力の3条件が揃うと、直接処理はメモリ枯渇で崩壊する。
  • キューは殺到したリクエストを並べ、実際に走る処理を1件に絞ってピークをならす。
  • 非力なVPSでは prefetch_count=1 の直列処理が、共倒れの並列より結果的に速く・安定する。
  • RabbitMQ の登場人物は Producer / Queue / Consumer の3人だけ。

関連記事

Next — メッセージキュー入門②
3キュー設計 — request / reply / cancel

なぜ箱を3つに分けたのか。特に「途中でキャンセルする」を、なぜ順番待ちの列をバイパスする独立キューにしたのか。 AIコンシェルジュの実装を題材に、設計判断の中身へ踏み込みます。