メインコンテンツまでスキップ

Servletコンテナ

Servlet APIが「契約(interface)」なら、Servletコンテナはその「実装者」です。Servletコンテナを理解することで、Webアプリケーションがどのように動いているかの全体像が見えてきます。


Servletコンテナとは

役割

┌─────────────────────────────────────────────────────────────┐
│ Servletコンテナの役割 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Servletコンテナ = Servlet API の実装 + 実行環境 │
│ │
│ 2つの顔を持つ: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. Servlet API の実装者 │ │
│ │ ・HttpServletRequest を実装したクラスを提供 │ │
│ │ ・HttpServletResponse を実装したクラスを提供 │ │
│ │ ・Filter, Listener の仕組みを提供 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 2. Webサーバーとしての実行環境 │ │
│ │ ・HTTPリクエストを受信 │ │
│ │ ・スレッドを割り当て │ │
│ │ ・Servletを呼び出し │ │
│ │ ・HTTPレスポンスを送信 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

なぜコンテナが必要か

┌─────────────────────────────────────────────────────────────┐
│ コンテナなしで作ると... │
├─────────────────────────────────────────────────────────────┤
│ │
│ 自分で全部実装する必要がある: │
│ │
│ □ TCPソケットのリッスン │
│ □ 複数接続の同時処理(スレッドプール) │
│ □ HTTPリクエストのパース │
│ ・リクエストライン解析 │
│ ・ヘッダー解析 │
│ ・ボディ解析(multipart, chunked等) │
│ □ Keep-Alive接続の管理 │
│ □ HTTPレスポンスの構築 │
│ □ SSL/TLS対応 │
│ □ セッション管理 │
│ □ 仮想ホスト対応 │
│ □ アクセスログ出力 │
│ □ ... │
│ │
│ → 本来作りたいビジネスロジックに集中できない │
│ → 車輪の再発明 │
│ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ コンテナがあると... │
├─────────────────────────────────────────────────────────────┤
│ │
│ インフラ層はコンテナに任せる: │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ あなたが書くこと │ │
│ │ ・doGet() の中身(ビジネスロジック) │ │
│ │ ・それだけ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ コンテナがやること │ │
│ │ ・HTTP通信の全て │ │
│ │ ・スレッド管理の全て │ │
│ │ ・セキュリティの基盤 │ │
│ │ ・セッション管理 │ │
│ │ ・ログ出力 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ → 関心の分離(Separation of Concerns) │
│ │
└─────────────────────────────────────────────────────────────┘

コンテナの責務

1. ネットワーク層

┌─────────────────────────────────────────────────────────────┐
│ ネットワーク層の責務 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【TCP接続管理】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・ポート(通常8080)でリッスン │ │
│ │ ・接続の受け入れ(accept) │ │
│ │ ・Keep-Aliveの管理 │ │
│ │ ・接続タイムアウト処理 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【HTTPパース】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ バイト列 → HTTPリクエストオブジェクト │ │
│ │ │ │
│ │ "GET /users?id=1 HTTP/1.1\r\n" │ │
│ │ "Host: example.com\r\n" │ │
│ │ "Accept: application/json\r\n" │ │
│ │ "\r\n" │ │
│ │ ↓ パース │ │
│ │ HttpServletRequest オブジェクト │ │
│ │ method: "GET" │ │
│ │ uri: "/users" │ │
│ │ parameters: {id: "1"} │ │
│ │ headers: {Host: "...", Accept: "..."} │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【HTTPレスポンス構築】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ HttpServletResponse オブジェクト → バイト列 │ │
│ │ │ │
│ │ status: 200 │ │
│ │ contentType: "application/json" │ │
│ │ body: {"name": "Alice"} │ │
│ │ ↓ 構築 │ │
│ │ "HTTP/1.1 200 OK\r\n" │ │
│ │ "Content-Type: application/json\r\n" │ │
│ │ "Content-Length: 17\r\n" │ │
│ │ "\r\n" │ │
│ │ "{\"name\":\"Alice\"}" │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

2. スレッド管理

┌─────────────────────────────────────────────────────────────┐
│ スレッド管理の責務 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【スレッドプール】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ リクエスト1 ─→ ワーカースレッド1 ─→ Servlet │ │
│ │ リクエスト2 ─→ ワーカースレッド2 ─→ Servlet │ │
│ │ リクエスト3 ─→ ワーカースレッド3 ─→ Servlet │ │
│ │ リクエスト4 ─→ (待機中...) │ │
│ │ ↑ │ │
│ │ スレッドプール │ │
│ │ (例: 最大200スレッド) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ なぜスレッドプール? │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・スレッド生成コストの削減(再利用) │ │
│ │ ・リソース枯渇の防止(上限設定) │ │
│ │ ・過負荷時のグレースフルな処理 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【設定可能なパラメータ】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・最小スレッド数(アイドル時も維持) │ │
│ │ ・最大スレッド数(これ以上は作らない) │ │
│ │ ・キュー長(最大スレッド使用中の待機数) │ │
│ │ ・タイムアウト(アイドルスレッドの破棄) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

3. ライフサイクル管理

┌─────────────────────────────────────────────────────────────┐
│ ライフサイクル管理の責務 │
├─────────────────────────────────────────────────────────────┤
│ │
│ コンテナがServletの生死を管理する(IoC) │
│ │
│ 【起動時】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. Servletクラスをロード │ │
│ │ 2. インスタンスを1つ作成 │ │
│ │ 3. init() を呼び出し │ │
│ │ 4. リクエスト受付開始 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【リクエスト時】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. スレッドプールからスレッドを取得 │ │
│ │ 2. HttpServletRequest/Response を作成 │ │
│ │ 3. Servlet.service() を呼び出し │ │
│ │ 4. スレッドをプールに返却 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【終了時】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. 新規リクエストの受付停止 │ │
│ │ 2. 処理中リクエストの完了を待機 │ │
│ │ 3. destroy() を呼び出し │ │
│ │ 4. リソース解放 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ グレースフルシャットダウン: │
│ ・処理中のリクエストを中断せず完了させる │
│ ・コンテナがこれを保証する │
│ │
└─────────────────────────────────────────────────────────────┘

4. セッション管理

┌─────────────────────────────────────────────────────────────┐
│ セッション管理の責務 │
├─────────────────────────────────────────────────────────────┤
│ │
│ HTTPはステートレス → コンテナがステートを管理 │
│ │
│ 【仕組み】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ブラウザ コンテナ │ │
│ │ │ │ │ │
│ │ │ ─── 初回リクエスト ───→ │ │ │
│ │ │ │ セッション作成 │ │
│ │ │ │ ID: abc123 │ │
│ │ │ ←── Set-Cookie ──────── │ │ │
│ │ │ JSESSIONID=abc123 │ │ │
│ │ │ │ │ │
│ │ │ ─── 2回目リクエスト ──→ │ │ │
│ │ │ Cookie: JSESSIONID │ セッション取得 │ │
│ │ │ │ ID: abc123 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【コンテナの責務】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・セッションIDの生成(ランダム、推測困難) │ │
│ │ ・Cookieの送受信 │ │
│ │ ・セッションオブジェクトの保存(メモリ/永続化) │ │
│ │ ・タイムアウト管理(期限切れセッションの破棄) │ │
│ │ ・クラスタ環境でのセッション共有(設定次第) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

5. セキュリティ

┌─────────────────────────────────────────────────────────────┐
│ セキュリティの責務 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【SSL/TLS】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・HTTPS接続の終端 │ │
│ │ ・証明書の管理 │ │
│ │ ・暗号化/復号化 │ │
│ │ ・プロトコルバージョン制御(TLS 1.2/1.3) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【基本認証】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・BASIC認証の処理 │ │
│ │ ・DIGEST認証の処理 │ │
│ │ ・レルム(認証領域)の管理 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【アクセス制御】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・IPベースのアクセス制限 │ │
│ │ ・URLパターンごとの制限 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ※ 高度な認証/認可は Spring Security 等で実装 │
│ │
└─────────────────────────────────────────────────────────────┘

主要なServletコンテナ

比較

┌─────────────────────────────────────────────────────────────┐
│ 主要なServletコンテナ比較 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Apache Tomcat │ │
│ │ ─────────────────────────────────────────────────── │ │
│ │ 開発元: Apache Software Foundation │ │
│ │ 歴史: 1999年〜(最も歴史が長い) │ │
│ │ 特徴: 安定性、実績、情報量 │ │
│ │ 用途: 汎用、Spring Bootのデフォルト │ │
│ │ 強み: 枯れている、トラブル時の情報が豊富 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Eclipse Jetty │ │
│ │ ─────────────────────────────────────────────────── │ │
│ │ 開発元: Eclipse Foundation │ │
│ │ 歴史: 1995年〜(Tomcatより古い) │ │
│ │ 特徴: 軽量、組み込み向け、非同期に強い │ │
│ │ 用途: 組み込み、マイクロサービス │ │
│ │ 強み: 起動が速い、メモリ効率 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Undertow │ │
│ │ ─────────────────────────────────────────────────── │ │
│ │ 開発元: Red Hat / JBoss │ │
│ │ 歴史: 2012年〜(比較的新しい) │ │
│ │ 特徴: 高パフォーマンス、ノンブロッキング │ │
│ │ 用途: 高負荷環境、WildFly │ │
│ │ 強み: スループット、低レイテンシ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

選び方

┌─────────────────────────────────────────────────────────────┐
│ どれを選ぶべきか │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【迷ったらTomcat】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・Spring Bootのデフォルト │ │
│ │ ・情報が豊富(StackOverflow等) │ │
│ │ ・大半のケースで十分な性能 │ │
│ │ ・「動かない」時に調べやすい │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【Jettyを選ぶ場合】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・起動時間を極限まで短くしたい │ │
│ │ ・メモリ使用量を抑えたい │ │
│ │ ・WebSocketを多用する │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【Undertowを選ぶ場合】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・高いスループットが必要 │ │
│ │ ・同時接続数が非常に多い │ │
│ │ ・WildFly/JBoss環境との統一 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 重要: Servlet仕様に準拠していれば、 │
│ コードを変えずにコンテナだけ差し替え可能 │
│ │
└─────────────────────────────────────────────────────────────┘

組み込みコンテナ vs スタンドアロン

2つのデプロイモデル

┌─────────────────────────────────────────────────────────────┐
│ デプロイモデルの違い │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【スタンドアロン(従来型)】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. コンテナを別途インストール │ │
│ │ └── /opt/tomcat/ │ │
│ │ │ │
│ │ 2. アプリをWARファイルにパッケージ │ │
│ │ └── myapp.war │ │
│ │ │ │
│ │ 3. WARをコンテナにデプロイ │ │
│ │ └── /opt/tomcat/webapps/myapp.war │ │
│ │ │ │
│ │ 4. コンテナを起動 │ │
│ │ └── /opt/tomcat/bin/startup.sh │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【組み込み(Spring Boot方式)】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. アプリにコンテナを含める │ │
│ │ └── 依存関係に tomcat-embed を追加 │ │
│ │ │ │
│ │ 2. JARファイルにパッケージ │ │
│ │ └── myapp.jar (コンテナ込み) │ │
│ │ │ │
│ │ 3. JARを実行 │ │
│ │ └── java -jar myapp.jar │ │
│ │ │ │
│ │ → コンテナのインストール不要 │ │
│ │ → アプリが自己完結 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

なぜ組み込みが主流になったか

┌─────────────────────────────────────────────────────────────┐
│ 組み込みコンテナが主流になった理由 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【スタンドアロンの問題】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・環境ごとにコンテナのバージョンが違う │ │
│ │ ・「開発環境では動くのに本番で動かない」 │ │
│ │ ・コンテナの設定ファイルが環境に散らばる │ │
│ │ ・デプロイ手順が複雑(WAR作成→転送→デプロイ) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【組み込みのメリット】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・「ビルドしたJARがそのまま本番で動く」 │ │
│ │ ・コンテナの設定もアプリに含まれる │ │
│ │ ・環境差異が減る │ │
│ │ ・Dockerとの相性が良い │ │
│ │ ・CI/CDパイプラインがシンプル │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【12-Factor Appとの関連】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ II. Dependencies │ │
│ │ → 依存を明示的に宣言(コンテナも含む) │ │
│ │ V. Build, release, run │ │
│ │ → ビルド成果物は不変 │ │
│ │ VII. Port binding │ │
│ │ → 自己完結型、ポートをバインド │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

コンテナのチューニング

主要な設定項目

┌─────────────────────────────────────────────────────────────┐
│ コンテナのチューニング項目 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 【スレッドプール】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 項目 │ 意味 │ │
│ │ ─────────────────┼─────────────────────────────── │ │
│ │ max-threads │ 最大スレッド数 │ │
│ │ min-spare-threads │ 最小スレッド数(アイドル時) │ │
│ │ accept-count │ 待機キューの長さ │ │
│ │ connection-timeout│ 接続タイムアウト │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【接続】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 項目 │ 意味 │ │
│ │ ─────────────────┼─────────────────────────────── │ │
│ │ max-connections │ 最大同時接続数 │ │
│ │ keep-alive-timeout│ Keep-Alive接続のタイムアウト │ │
│ │ max-keep-alive │ Keep-Aliveで許可するリクエスト数│ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 【考え方】 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ・CPUバウンド → スレッド数 ≒ CPUコア数 │ │
│ │ ・I/Oバウンド → スレッド数を増やす価値あり │ │
│ │ ・メモリ制約 → スレッド数に上限 │ │
│ │ │ │
│ │ 最適値は負荷テストで決める(机上の計算では限界) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

まとめ

Servletコンテナの責務

責務内容
ネットワーク層TCP接続、HTTPパース、レスポンス構築
スレッド管理スレッドプール、リクエストのディスパッチ
ライフサイクルServletの生成・初期化・破棄
セッション管理Cookie、セッションオブジェクト
セキュリティSSL/TLS、基本認証

理解すべきこと

┌─────────────────────────────────────────────────────────────┐
│ Servletコンテナを理解して得られること │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. Webアプリの「実行環境」が何をしているか分かる │
│ ・スレッドプール、Keep-Alive、セッション... │
│ │
│ 2. パフォーマンス問題の原因を特定できる │
│ ・スレッド枯渇、接続タイムアウト... │
│ │
│ 3. 適切なコンテナを選択できる │
│ ・Tomcat vs Jetty vs Undertow │
│ │
│ 4. デプロイモデルの違いを理解できる │
│ ・WAR vs 組み込みJAR │
│ │
└─────────────────────────────────────────────────────────────┘

次のステップ