Webアプリケーションの処理モデルについて

システムアーキテクチャ

この記事では、Webアプリケーションの代表的な処理モデルについて書く。

  • イベントループ
  • スレッド
  • プロセス

各処理モデルの概要

イベントループモデル(非同期・シングルスレッド)

  • 特徴:1つのスレッドで、イベント(非同期IOなど)を順に処理
  • 代表例:Node.js、Deno、JavaScript(ブラウザ)
[ イベントキュー ] → [ イベントループ ] → [ 実行 ]

メリット

  • メモリ効率が良い(スレッドやプロセスを大量に作らない)
  • IO待ちを他の処理に活用できる

デメリット

  • 重い計算(CPUバウンド)でブロックしやすい

スレッドモデル(マルチスレッド)

  • 特徴:1プロセスの中で、複数のスレッドが並行に動作
  • 代表例:Java、Python、Go(内部的にスレッドスケジューラ)
[ プロセス ]
 ├─ スレッド1(リクエストA)
 ├─ スレッド2(リクエストB)
 └─ ...

メリット

  • CPUバウンド処理に強い(マルチコア活用可能)

デメリット

  • スレッドごとのリソース消費や、競合状態の管理が必要

プロセスモデル(マルチプロセス)

  • 特徴:各リクエストを別プロセスで処理。完全に分離されている
  • 代表例:PHP(FPM)、Ruby(複数Pumaプロセス)、Sidekiq(マルチプロセス構成)
[ リクエストA ] → [ プロセス1 ]
[ リクエストB ] → [ プロセス2 ]

メリット

  • 安定性が高い(他の処理の影響を受けにくい)

デメリット

  • メモリ効率が悪い(プロセスごとに環境を持つ)

言語ごとの処理モデルと向き・不向き

言語 処理モデル IOバウンド CPUバウンド 備考
Node.js イベントループ(非同期) IO特化、重い計算処理は注意
Go goroutine + M:Nスレッド 軽量スレッドで並列・並行処理が得意
Ruby(MRI) スレッド+GIL(制限あり) GILによりCPU処理は実質シングルスレッド
PHP(FPM) プロセス(1リクエスト=1プロセス) プロセス分離で安定だがオーバーヘッド大
Java マルチスレッド JVMレベルでのスレッド最適化
Python(CPython) スレッド+GIL(制限あり) RubyのMRIと同様にGILの制約あり

各処理モデルの実装上の注意点

イベントループモデル

  • 単一スレッド:真の並列処理は不可能
  • ノンブロッキングIO:同期処理はイベントループをブロックする
  • CPUバウンド回避:重い計算はWorker Threadsや外部プロセスに委譲

スレッドモデル

  • 競合状態:共有メモリへの同時アクセス制御が必要
  • デッドロック:複数ロックの取得順序に注意
  • メモリオーバーヘッド:スレッドあたり約1-8MBのスタック領域

プロセスモデル

  • プロセス間通信:IPCやファイルシステム経由での連携
  • メモリ使用量:各プロセスが独立したメモリ空間を持つ
  • 起動コスト:プロセス作成はスレッド作成より重い

まとめ

各処理モデルには一長一短があり、アプリケーションの特性や要件に応じて適切なモデルを選択することが重要である。IOバウンドな処理が多い場合はイベントループモデル、CPUバウンドな処理が多い場合はスレッドモデルやプロセスモデルが適していると考えられる。また、言語ごとの特性も考慮すると良い。