Tomcat で静的リソースを処理するチュートリアル

Tomcat で静的リソースを処理するチュートリアル

序文

Tomcat 内のすべてのリクエストは Servlet によって処理され、静的リソースも例外ではありません。デフォルトの web.xml では、キャッシュとブレークポイントの再開をサポートする静的リソースを処理するように DefaultServlet が設定されています。

DefaultServlet の基本的な処理は次のとおりです。

  • リソースがキャッシュされているかどうかを確認する
  • オプションのIfヘッダーフィールドで指定された条件が満たされているかどうかを確認します。
  • Content-Type、Content-Length、ETag、Last-Modifiedなどのレスポンスヘッダーフィールドを設定する
  • Sendfileの条件が満たされているかどうかを確認し、満たされていない場合はコンテンツを出力ストリームにコピーします。

次に、リソース キャッシュの設計と実装、および If ヘッダー フィールドの処理を主に分析します。

1. リソースキャッシュの設計

ディスクへのアクセス速度はメモリへのアクセス速度よりもはるかに遅いため、一部の静的リソースを適切にキャッシュすると、システムの応答が速くなります。

Tomcat がバージョン 6.0.53 で静的リソースの処理を実装したとき、いくつかの JNDI API が使用されました (ただし、使用時に JNDI とはほとんど関係がないと感じられました)。関連するクラス ダイアグラムとコア メソッドおよびプロパティは次のとおりです。

キャッシュ関連クラス:

  • ResourceCache: リソースの検索、読み込み、破棄機能を提供するキャッシュ実装
  • CacheEntry: キャッシュエントリ。キャッシュ名(/tomcat.gifなど)、リソースとリソース属性、対応するディレクトリが含まれます。

リソース ディレクトリ関連のクラスは次のとおりです。

  • EmptyDirContext: 主に埋め込みモードで使用され、リソースが利用できないかのように動作します。
  • FileDirContext: ファイル システム ベースのリソース ディレクトリ サービス
  • WARDirContext: war ファイルに基づくディレクトリ サービス
  • リソース: 主にバイトデータと入力ストリームなどのリソースコンテンツをカプセル化します。
  • ResourceAttributes: リソース属性、主にコンテンツの長さと最終更新時刻
  • ProxyDirContext: リソース キャッシュとディレクトリ サービスのプロキシ。リソース キャッシュの検索や、キャッシュの有効期限が切れていないかどうかの確認などの機能を提供します。

デフォルトでは、キャッシュの最大サイズは 10 MB、キャッシュされた単一のリソースの最大サイズは 512 KB、キャッシュ TTL は 5 秒です。

通常、Mapper が静的リソースを処理する Wrapper にマップされると、リソースの読み込みが発生します。基本的なメソッド呼び出しは次のとおりです。

Mapper.map(メッセージバイト、メッセージバイト、マッピングデータ)
└─Mapper.internalMap(CharChunk、CharChunk、MappingData)
 └─Mapper.internalMapWrapper(Mapper$Context、CharChunk、MappingData)
 └─ProxyDirContext.lookup(文字列)
 └─ProxyDirContext.cacheLookup(文字列)
 └─ResourceCache.lookup(文字列)
  └─ResourceCache.find(CacheEntry[], 文字列)

キャッシュ リソースは、順番に内部配列に挿入されます。find メソッドは、リソース名でキャッシュ内でバイナリ検索を実行します。リソース名はリクエスト パスです。キャッシュ ヒットとキャッシュ ミスの 2 つの状況があります。

キャッシュが見つからない場合、cacheLookup メソッドで新しい CacheEntry オブジェクトが作成され、cacheLoad メソッドが呼び出されて、それが ResourceCache のキャッシュ配列に追加されます。追加する前に、キャッシュ エントリに対して次の操作が実行されます。

  • キャッシュ リソース プロパティ (主にファイルの contentLength と lastModified) を取得して初期化します。
  • ファイルの長さが512KB未満の場合、ファイルの内容をメモリにロードします。
  • キャッシュを既存としてマークし、キャッシュのタイムスタンプを設定する

キャッシュヒットはキャッシュエントリを検証します:

  • 期限が切れていないか、現在の時刻がキャッシュエントリに設定されたタイムスタンプより大きいかどうかを確認します。
  • 期限が切れている場合は、リソースの内容が変更されていないか確認してください。
  • 変更された場合は、このキャッシュをクリアして最新のコンテンツをお読みください

上記はリソースキャッシュの簡単な処理プロセスです。

2. Ifヘッダーフィールドの処理

クライアントは要求されたリソースを受信して​​キャッシュします。リソースを再度要求すると、サーバーは特定の要求ヘッダー フィールドに基づいてリソースが変更されたかどうかを確認します。変更がない場合は、304 Not Modified 応答のみが返されます。変更がない場合は、リソースのコンテンツが返されるため、帯域幅が節約されます。

リソース検証に使用されるヘッダー フィールドは、Last-Modified+If-Modified-Since と ETag+If-None-Match の 2 つです。

Last-Modified+If-Modified-Since、単位は秒です。これは理解しやすいです。サーバー リソースの最終変更時刻が If-Modified-Since の値より小さい場合、リソースが変更されていないことを意味します。 If-Modified-Since に対応するのは、アサーションに似た If-Unmodified-Since です。これより小さいタイムスタンプを持つリソースのみが返されます。タイムスタンプがこれより大きいか等しい場合は、412 Precondition Failed エラーが返されます。

タイムスタンプ検証を使用すると、いくつかの欠点があります。

  • ファイルは変更時刻のみ変更される可能性があり、内容は変更されません。
  • 1秒未満で変更されたファイルは判断できない
  • サーバーはファイルの最終変更時刻を正確に取得できない可能性があります。

そのため、HTTP では ETag が導入されました。 ETag (エンティティ タグ) はリソースの一意の識別子であり、リソースに対してサーバーによって生成されたトークンと見なすことができ、リソースが変更されたかどうかを確認するために使用されます。 HTTP では、ETag を二重引用符で囲む必要があると規定されているだけで、その内容や実装方法については規定されていません。Tomcat の ETag 生成ロジックは"W/\"" + contentLength + "-" + lastModified + "\""です。ここで、'W/' は大文字と小文字の区別を示します。

ETag+If-None-Match。If-None-Match の値は、コンマで区切られた 1 つ以上の ETag で構成されます。サーバー リソースの ETag がいずれにも一致しない場合は、要求されたリソースが変更されたことを意味します。それ以外の場合は、変更はありません。また、アスタリスク (*) という特別な値もあります。これは、リソースをアップロードするとき (通常は PUT メソッド) にのみ使用され、アップロードされたかどうかを確認します。

さらに、If-None-Match は If-Modified-Since よりも優先度が高く、つまり If-None-Match が存在する場合、最終変更時刻はチェックされません。 If-None-Match の反対は If-Match で、これもアサーションに似ています。リソースの ETag が一致する場合にのみ変更されていないと見なされます。通常は、ブレークポイントの再開に使用されます。

この部分を実装するための Tomcat のコア コードは次のとおりです。

// リソースが変更されたとみなされる場合にのみtrueを返す protected boolean checkIfHeaders(HttpServletRequest request,
  HttpServletResponse レスポンス、ResourceAttributes リソース属性)
  IOException をスローします {
 checkIfMatch(リクエスト、レスポンス、リソース属性) を返します
  && checkIfModifiedSince(リクエスト、レスポンス、リソース属性)
  && checkIfNoneMatch(リクエスト、レスポンス、リソース属性)
  && checkIfUnmodifiedSince(リクエスト、レスポンス、リソース属性);
}

2.1 ワンタイムリクエストプロセス

/main.css 静的リソースのリクエストを例にとると、最初のリクエスト応答ヘッダー情報は次のようになります。

HTTP/1.1 200 OK
サーバー: Apache-Coyote/1.1
受け入れ範囲: バイト
ETag: W/"72259-1557127244000"
最終更新日: 2019年5月6日(月) 07:20:44 GMT
コンテンツタイプ: text/css
コンテンツの長さ: 72259
日付: 2019年5月6日月曜日 07:20:57 GMT

2 番目のリクエストを行うときは、まずリクエスト ヘッダー フィールドの重要な情報を確認します。

キャッシュ制御:max-age=0
接続:キープアライブ
ホスト:localhost:8080
変更日時: 2019 年 5 月 6 日 (月) 07:20:44 GMT
一致しない場合:W/"72259-1557127244000"

リクエストを受信すると、サーバーは ETag を比較します。一致した場合、リソースは変更されていないことを意味します。応答は次のようになります。

HTTP/1.1 304 変更されていません
サーバー: Apache-Coyote/1.1
ETag: W/"72259-1557127244000"
日付: 2019年5月6日月曜日 07:21:46 GMT

注意: 再生する場合はテキストタイプを使用してください。Chrome ブラウザを使用する場合は、キャッシュを有効にすることを忘れないでください。

2.2 受け入れ範囲

上記の応答では、サーバーは Accept-Ranges: bytes ヘッダーを設定します。これは文字通り、リソースのバイトの一部を要求できることを意味します。クライアントがこのヘッダーを見つけると、転送を再開しようとすることができます。

解析処理は HTTP 仕様の実装です。ここでは詳細に分析しません。仕様の詳細については、RFC7233#section-2.3 を参照してください。

3. SendFile処理

SendFile がサポートされているかどうかを確認します。この操作は NIO モード、つまりゼロ コピーでサポートされます。この操作により、アプリケーション メモリへのコピーが 1 つ削減され、カーネルからチャネルに直接データが書き込まれます。 Tomcat は 48KB を超えるファイルを送信するためにこのメソッドを使用しようとします。

4. まとめ

Tomcat の静的リソース処理の実装は比較的完成していますが、Nginx などの Web サーバーでは静的リソースを直接処理できるのに対し、Tomcat では追加のマッピングを行う必要があるため、それに比べるとまだ若干劣っています。一般的に、Tomcat が動的リクエストの処理に集中できるように、動的と静的の分離が実行されます。

要約する

以上がこの記事の全内容です。この記事の内容が皆様の勉強や仕事に何らかの参考学習価値をもたらすことを願います。123WORDPRESS.COM をご愛顧いただき、誠にありがとうございます。

以下もご興味があるかもしれません:
  • Tomcat が CSS や JS などの静的リソース ファイルをロードできない場合の解決策
  • 動的データと静的リソースのリクエストを分離するための Nginx + Tomcat の詳細な説明

<<:  Linux での MySQL 5.1 および 5.7 のインストール チュートリアル

>>:  Reactの新バージョンのライフサイクルフック機能と使用方法の詳細な説明

推薦する

FileZilla Server の FTP サーバー構成と 425 エラーおよび TLS 警告の解決策の詳細な説明

123WORDPRESS.COM では、FileZilla のダウンロード リンクを提供しています:...

Docker イメージのプルとタグ操作 pull | tag

Fabric プロジェクトのソースコードを読み直してみたところ、Docker の部分でよくわからな...

react+reduxを使用してカウンター機能を実装すると発生する問題

Redux はシンプルな状態マネージャーです。その歴史をたどることはしません。使用法の観点から見ると...

フロントエンド Vue ユニットテストを始める

目次1. ユニットテストはなぜ必要なのでしょうか? 2. ユニットテストの書き方3. テストツール4...

Vue の新しいおもちゃ VueUse の具体的な使い方

目次序文VueUseとは使いやすいおなじみの手ぶれ補正やスロットル機能もありますグローバル状態を共有...

nginx で http でアクセスする Web サイトを https に変更する方法

目次1. 背景2. 前提条件https:証明書システム: 3. 操作プロセス3.1 証明書の生成3....

CentOS 6.8 に MySQL 8.0.18 をインストールするチュートリアルの簡単な分析 (RPM 方式)

今日は、CentOS 6.8 サーバーに MySQL 8.0.18 をインストールする方法を記録しま...

Vueはシンプルなタイマーコンポーネントを実装します

プロジェクトを実行すると、リアルタイム更新、広告アニメーションの連続表示などの要件に遭遇することは避...

Dockerはelasticsearchイメージを起動し、ディレクトリをマウントした後にエラーを解決します

docker hub から es イメージ (バージョン 6.4.2) をダウンロードしました。詳細...

Nginx URL 書き換えメカニズムの原理と使用例

URL 書き換えは、Web サイトの優先ドメインを決定するのに役立ちます。同じリソース ページの複数...

JavaScript キャンバスでカラフルな太陽のハロー効果を実現

この記事では、カラフルな太陽のハロー効果を実現するためのJavaScriptキャンバスの具体的なコー...

Vue3スタイルのCSS変数注入の実装

目次まとめ基本的な例モチベーションデザインの詳細コンパイルの詳細採用戦略練習するヒント適切なプロパテ...

MySQL テーブルの断片化を解消し、スペースを再利用する方法

目次MySQL テーブルの断片化の原因行の断片化行内断片化空き領域の断片化MySQL で極度に断片化...

XHTML 入門チュートリアル: Web ページのヘッダーと DTD

ヘッドと DTD はページには表示されませんが、Web ページの重要な要素です。 なぜ失敗したのでし...

Win10 は Tsinghua ソースを使用して pytorch-GPU バージョンをすばやくインストールします (推奨)

Cudaがインストールされているかどうかを確認してくださいアナコンダプロンプトに入力nvcc -V...