WebFluxとServlet(Tomcat)を比較してみる

はじめに

Spring Boot で REST API を開発する際、多くの場合 spring-web(=サーブレットベース)を用いることが一般的です。私も普段はその方法で開発をしていましたが、先日ある API の負荷テストを行った際に、思ったよりスループットが出ないことに気づきました。

その API は単純に 200ms の外部リクエストを待つだけのものでしたが、スレッド数の制約によりリクエスト処理にボトルネックが発生していたのです。実際の業務ロジックではさらに複雑な条件分岐や DB アクセス、複数の API 呼び出しが重なるため、レスポンスタイムはもっと長くなります。

そこで今回、Spring Boot で提供されているもう1つの選択肢である Spring WebFlux を使って、非同期I/Oサーバーとの比較検証を行ってみました。

実験の前提条件

今回の検証では、次の構成で API を実装しました。

hey:500並列リクエスト

reserve API

1000ms sleep or delay

200 OK 応答

Servlet (Tomcat) の結果

$ hey -n 1000 -c 500 -m POST http://localhost:8085/reserve

結果概要

Tomcat の maxThreads デフォルト値(200)に対して、500並列リクエストを投げたため、スレッドが枯渇し一部のリクエストは待機列に入りました。結果として、RPSは200未満にとどまり、全体の応答時間が大きく延びています。

Spring WebFlux (Netty) の結果

$ hey -n 1000 -c 500 -m POST http://localhost:8085/reserve

結果概要

同じ処理を WebFlux で実装した場合、約2倍のスループットが得られました。イベントループによる非同期処理によって、スレッド枯渇が発生せず、ほぼ理論通りのレスポンスタイムとなっています。

Servlet と WebFlux の処理モデルの違い

Servlet(Tomcat)はスレッドベース

サーブレットベースのアプリケーションは、「リクエスト1件につきスレッド1本」というスレッド・パー・リクエストモデルで動作します。

WebFlux(Netty)はイベントループベース

一方、WebFlux(Netty)はイベントループと非同期I/Oを基盤とした処理モデルです。

nginxとの類似性とモデル比較

「イベントループ」と聞いて思い出すのは nginx です。Nginx も epollkqueue といった OSレベルの非同期I/Oを活用し、少数のワーカープロセスで **大量同時接続(C10K問題)**を処理するために設計されたサーバーです。

WebFlux(Netty)もまさにこのモデルに近く、Java 版 nginx 的な非同期処理モデルといえます。

goroutine/virtual threadとの比較

WebFluxと似たような概念として、Java の Virtual Thread(仮想スレッド) や Go の goroutineがあります。 これらも大量の同時処理を可能にするモデルですが、WebFluxとは実行方式が異なります。

特性goroutine / Virtual ThreadWebFlux / Nginx
処理単位軽量スレッド(M:Nモデル)イベントループ + 非同期イベント
ブロッキング許容✅(sleep, I/O可)❌(ブロッキング禁止)
書き方同期コードでOK非同期チェーン(Mono/Flux)
実行制御ランタイムがスケジューリングイベントループがコールバック制御

結論:

おわりに:WebFluxはServletの代替となるか?

WebFlux は優れたスケーラビリティを持ちますが、必ずしも Servlet の完全な上位互換ではありません。記述スタイルが非同期中心になることで、コードの見通しが悪くなったり、エラーハンドリングが複雑になることもあります。

適しているケースモデル
通常の業務Webアプリ(管理画面など)Servlet(Tomcat)
高同時接続/外部I/O待ちが多いAPIWebFlux(Netty)

開発するシステムの性質やチームの技術スタックに応じて、適切なアーキテクチャを選択していくことが重要です。

Ko-fi donationsSupport Me on Ko-fi