Tomcatの動作原理を分析する

Tomcatの動作原理を分析する

SpringBoot は巨大な Python のようで、ゆっくりと私たちの周りを巻きつき、麻痺させます。 SpringBoot の使用によって確かに作業効率が向上しましたが、同時に多くのスキルを忘れてしまったことも認めざるを得ません。私がこの業界に入った当初は、Java Web プロジェクトを Tomcat 経由で手動でデプロイし、Tomcat のパフォーマンス チューニングを頻繁に行っていました。さらに、サービス起動例外を引き起こす可能性のある Jar の損失やバージョンの競合などの問題を回避するために、さまざまな Jar 間の関係を整理する必要もあります。現在、これらの面倒で反復的なタスクはすべて SpringBoot によって処理されており、ビジネス ロジックにさらに集中できます。ただし、Tomcat の動作原理とリクエスト処理フローを理解することは、Spring フレームワークのソース コードを分析することと同じくらい重要です。少なくとも面接官は、これらの基本原則や設計のアイデアについて特に質問したがります。この記事が少しでもお役に立てれば幸いです。

Tomcat の全体的なアーキテクチャ

Tomcat は、無料のオープンソースの軽量 Web アプリケーション サーバーです。同時実行性がそれほど高くない中小企業のプロジェクトでの使用に適しています。

ファイルディレクトリ構造

以下はTomcat 8のメインディレクトリ構造です。

目次機能説明
ビンスタートアップやシャットダウンなどの実行可能ファイルを保存します
会議コア構成ファイル server.xml やアプリケーションのデフォルトのデプロイメント記述子ファイル web.xml などの構成ファイルを格納します。
ライブラリTomcatの実行に必要なjarパッケージを保存する
ログ実行中のログファイルを保存します
ウェブアプリデフォルトのWebアプリケーション展開ディレクトリを保存します
仕事Web アプリケーションのコード生成とコンパイルされたファイルを保存するための一時ディレクトリ

機能コンポーネント構造

Tomcat には、外部要求の受信と応答を担当するコネクタと、要求の処理を担当するコンテナという 2 つのコア機能があります。コネクタとコンテナは互いに補完し合い、基本的な Web サービスを構成します。各 Tomcat サーバーは複数のサービスを管理できます。

コンポーネント関数
コネクタ外部からのフィードバック要求を受け取る責任があります。 Tomcat と外部世界との間のトランスポート ハブです。ポートをリッスンして外部リクエストを受信し、処理されたリクエストをビジネス処理用のコンテナーに渡し、最終的にコンテナー処理の結果を外部にフィードバックします。
容器ビジネスロジックの内部処理を担当します。これは、サーブレット関連のロジックを管理および呼び出すために使用される、エンジン、ホスト、コンテキスト、ラッパーの 4 つのコンテナーで構成されています。
サービス外部に提供されるWebサービス。主にコネクタとコンテナという 2 つのコア コンポーネントと、その他の機能コンポーネントが含まれます。 Tomcat は複数のサービスを管理でき、各サービスは互いに独立しています。

Tomcatコネクタの基本原理

Tomcat コネクタ フレームワーク - Coyote

コネクタコア機能

1. ネットワーク ポートをリッスンし、ネットワーク要求を受信して​​応答します。

2. ネットワーク バイト ストリーム処理。受信したネットワーク バイト ストリームを Tomcat Request に変換し、次にコンテナへの標準 ServletRequest に変換します。同時に、コンテナから送信された ServletResponse を Tomcat Response に変換し、次にネットワーク バイト ストリームに変換します。

コネクタモジュール設計

コネクタの 2 つのコア機能を満たすには、ポートをリッスンする通信エンドポイント、ネットワーク バイト ストリームを処理するプロセッサ、そして最後に、処理された結果をコンテナーに必要な構造に変換するアダプタが必要です。

コンポーネント関数
終点エンドポイント。ソケットの受信と送信のロジックを処理するために使用されます。内部的には、Acceptor がリクエストをリッスンし、Handler がデータを処理し、AsyncTimeout がリクエストのタイムアウトをチェックします。具体的な実装としては、NioEndPoint、AprEndpoint などがあります。
プロセッサTomcat のリクエストおよびレスポンス オブジェクトの構築を担当するプロセッサ。具体的な実装としては、Http11Processor、StreamProcessor などがあります。
アダプタTomcat のリクエスト、レスポンスと ServletRequest、ServletResponse 間の変換を実現するアダプタ。これは、従来のアダプタ設計パターンを使用します。
プロトコルハンドラプロトコル プロセッサは、さまざまなプロトコルと通信方法を対応するプロトコル プロセッサに組み合わせます。たとえば、Http11NioProtocol は HTTP + NIO をカプセル化します。

対応するソース パッケージ パスはorg.apache.coyoteです。対応する構造図は以下のとおりです

Tomcatコンテナの基本原則

Tomcat コンテナ フレームワーク - Catalina

コンテナ構造解析

各サービスにはコンテナが含まれます。コンテナ エンジンは複数の仮想ホストを管理できます。各仮想ホストは複数の Web アプリケーションを管理できます。各 Web アプリケーションには複数のサーブレット ラッパーがあります。エンジン、ホスト、コンテキスト、ラッパーの 4 つのコンテナーは親子関係にあります。

容器関数
エンジンエンジンは複数の仮想ホストを管理します。
ホストWeb アプリケーションの展開を担当する仮想ホスト。
コンテクスト複数のサーブレット ラッパーを含む Web アプリケーション。
ラッパーラッパー、コンテナの最下層。サーブレットをカプセル化し、インスタンスの作成、実行、破棄を担当します。

対応するソース パッケージ パスはorg.apache.coyoteです。対応する構造図は以下のとおりです

コンテナ要求処理

コンテナのリクエスト処理プロセスは、エンジン、ホスト、コンテキスト、ラッパーの 4 つのコンテナをレイヤーごとに呼び出し、最後にサーブレット内の対応するビジネス ロジックを実行することです。各コンテナーにはチャネル パイプラインがあり、各チャネルにはリクエストとレスポンスの処理に使用されるゲートに似た基本バルブ (StandardEngineValve など) があります。フローチャートは以下のとおりです。

Tomcat リクエスト処理フロー

上記の知識ポイントでは、Tomcat がリクエストを処理する方法について少しずつ紹介しました。簡単に言えば、コネクタの処理フロー + コンテナの処理フロー = Tomcat の処理フローです。はっ!そこで疑問になるのが、Tomcat はどのようにしてリクエスト パスを通じて対応する仮想サイトを見つけるのかということです。対応するサーブレットを見つけるにはどうすればいいですか?

マッパー関数の紹介

ここでは、上記で紹介されていないコンポーネント マッパーを紹介する必要があります。名前が示すように、その役割はリクエストパスのルーティング マッピングを提供することです。リクエスト URL が照合され、どのコンテナーが一致を処理するかが決定されます。各コンテナーには、MappedHost などの対応する Mapper がそれぞれあります。 Mapper クラスが見つからないことで支配される恐怖を思い出したことがあるでしょうか。以前は、完全な関数を記述するたびに、web.xml でマッピング ルールを構成する必要がありました。ファイルが大きくなるにつれて、さまざまな問題が発生していました。

HTTP リクエストプロセス

tomcat/conf ディレクトリの server.xml ファイルを開き、http://localhost:8080/docs/api リクエストを分析します。

ステップ 1: コネクタはポート 8080 をリッスンします。要求されたポートとリスニング ポートが同じであるため、コネクタは要求を受け入れます。

ステップ 2: エンジンのデフォルトの仮想ホストは localhost であり、仮想ホストのディレクトリは webapps です。したがって、リクエストは tomcat/webapps ディレクトリを見つけました。

ステップ 3: 解析されたドキュメントは、Web プログラムのアプリケーション名 (コンテキスト) です。この時点で、リクエストは webapps ディレクトリの下の docs ディレクトリの検索を続行します。アプリケーション名を省略する場合もあります。

ステップ 4: 解析された API は、特定のビジネス ロジック アドレスです。このとき、docs/WEB-INF/web.xml からマッピング関係を見つけて、最終的に特定の関数を呼び出す必要があります。

<?xml バージョン="1.0" エンコーディング="UTF-8"?>
<サーバーポート="8005" シャットダウン="シャットダウン">

 <サービス名="Catalina">

	<!-- コネクタのリスニング ポートは 8080 で、デフォルトの通信プロトコルは HTTP/1.1 です -->
 <コネクタ ポート="8080" プロトコル="HTTP/1.1"
  接続タイムアウト = "20000"
  リダイレクトポート="8443" />
			 
	<!-- Catalina というエンジンのデフォルトの仮想ホストは localhost です -->
 <エンジン名="Catalina" defaultHost="localhost">

	 <!-- ディレクトリが webapps である localhost という名前の仮想ホスト -->
 <ホスト名="localhost" appBase="webapps"
  unpackWARs="true" autoDeploy="true">

 </ホスト>
 </エンジン>
 </サービス>
</サーバー> 

SpringBoot に埋め込まれた Tomcat を起動する方法

SpringBoot のワンクリック サービス起動機能により、社会に出たばかりの多くの友人は Tomcat が何であるかを忘れてしまいます。ハードウェアの性能がますます高くなるにつれて、一般的な中小規模のプロジェクトは、組み込みの Tomcat を使用して直接開始できるようになります。ただし、一部の大規模なプロジェクトでは Tomcat のクラスタリングとチューニングが必要になる場合があり、組み込みの Tomcat ではニーズを満たせない可能性があります。

まず、ソース コードから SpringBoot が Tomcat を起動する方法を分析してみましょう。以下は SpringBoot 2.x のコードです。

コードは main メソッドから始まり、run メソッドを実行してプロジェクトを開始します。

SpringApplication.run

実行メソッドをクリックし、アプリケーション コンテキストを更新するメソッドを見つけます。

this.prepareContext(コンテキスト、環境、リスナー、アプリケーション引数、印刷されたバナー);
this.refreshContext(コンテキスト);
this.afterRefresh(コンテキスト、アプリケーション引数);

refreshContext メソッドをクリックして、refresh メソッドを見つけます。そして、レイヤーごとに検索して、親クラスのメソッドを見つけます。

this.refresh(コンテキスト);

AbstractApplicationContext クラスの refresh メソッドには、子コンテナーを呼び出して更新するロジック ラインがあります。

BeanFactory を postProcess します。
beanFactory を呼び出します。
BeanPostProcessors を登録します。
メッセージソースを初期化します。
アプリケーションイベントマルチキャスターを初期化します。
this.onRefresh();
リスナーを登録します。
beanFactory の初期化を終了します。
this.finishRefresh();

onRefresh メソッドをクリックし、ServletWebServerApplicationContext の実装メソッドを見つけます。ここでようやく希望が見えてきました。

保護されたvoid onRefresh() {
 スーパーのonRefresh();

 試す {
 この.createWebServer();
 } キャッチ (Throwable var2) {
 throw new ApplicationContextException("Web サーバーを起動できません", var2);
 }
}

createWebServer メソッドをクリックし、ファクトリ クラスから WebServer を取得するためのコードを見つけます。

(webServer == null && servletContext == null)の場合{
 ServletWebServerFactory ファクトリー = this.getWebServerFactory();
 // Webサーバーを取得する 
 this.webServer = factory.getWebServer(新しい ServletContextInitializer[]{this.getSelfInitializer()});
} そうでない場合 (servletContext != null) {
 試す {
 // Webサーバーを起動する
 this.getSelfInitializer().onStartup(servletContext);
 } キャッチ (ServletException var4) {
 新しい ApplicationContextException("サーブレット コンテキストを初期化できません", var4); をスローします。
 }
}

getWebServer メソッドをクリックし、Jetty と Undertow も含まれる TomcatServletWebServerFactory の実装メソッドを見つけます。基本的なコネクタ、エンジン、仮想サイトなどがここで構成されます。

パブリック WebServer getWebServer(ServletContextInitializer... 初期化子) {
 Tomcat の tomcat = new Tomcat();
 ファイル baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
 tomcat.setBaseDir(baseDir.getAbsolutePath());
 コネクタ コネクタ = new Connector(this.protocol);
 tomcat.getService().addConnector(コネクタ);
 this.customizeConnector(コネクタ);
 tomcat.setConnector(コネクタ);
 tomcat.getHost().setAutoDeploy(false);
 tomcat のエンジンを設定します。
 イテレータ var5 = this.additionalTomcatConnectors.iterator();

 while(var5.hasNext()) {
 コネクタ additionalConnector = (Connector)var5.next();
 tomcat.getService().addConnector(追加のコネクタ);
 }

 this.prepareContext(tomcat.getHost(), 初期化子);
 this.getTomcatWebServer(tomcat) を返します。
} 

サービスが開始されると、ログが印刷されます

osbwembedded.tomcat.TomcatWebServer: Tomcat はポート 8900 (http) で初期化されました
o.apache.catalina.core.StandardService : サービスを開始しています [Tomcat]
org.apache.catalina.core.StandardEngine: サーブレット エンジンを起動しています: Apache Tomcat/8.5.34
oacatalina.core.AprLifecycleListener: 最適な実行を可能にする APR ベースの Apache Tomcat ネイティブ ライブラリ。
oaccC[Tomcat].[localhost].[/] : Spring組み込みWebApplicationContextを初期化しています
osweb.context.ContextLoader: ルート WebApplicationContext: 初期化が 16858 ミリ秒で完了しました

終わり

記事はここで終わりです。もう我慢できません。週末に一日中書いていましたが、まだソースコード部分にたどり着いていません。次の章に載せるしかありません。もう一度書いても無駄になります。間違っているところがあれば指摘してください。

上記はTomcatの動作の詳細な内容です。Tomcatの動作原理の詳細については、123WORDPRESS.COMの他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • Tomcat コアコンポーネントとアプリケーションアーキテクチャの詳細な説明
  • SpringBoot組み込みTomcatの起動原理の詳しい説明
  • TomcatはXMLを解析し、リフレクションを通じてオブジェクトを作成します。
  • Tomcat の startup.bat 原則の詳細な分析
  • Tomcat における catalina.bat 原理の詳細な分析
  • Tomcat データ ソースの原理、構成、使用方法の紹介
  • Tomcatホットデプロイメントの実装原理の詳細な説明
  • Tomcatアーキテクチャの原則をアーキテクチャ設計に分析する

<<:  MySQL でデータを削除してもテーブル ファイルのサイズが変更されないのはなぜですか?

>>:  VueのRender関数

推薦する

Ubuntu システムにおける Mysql ERROR 1045 (28000): ユーザー root@localhost へのアクセスが拒否される問題の解決方法

最初の方法: skip-grant-tables: 非常に便利なmysql起動パラメータ非常に便利な...

HTMLにビデオを挿入してすべてのブラウザと互換性を持たせる方法

HTML にビデオを挿入するために最もよく使用される方法は 2 つあります。1 つは古い <o...

three.js で 3D ダイナミック テキスト効果を実現する方法

序文みなさんこんにちは。CSS ウィザードの alphardex です。以前、海外のウェブサイトを閲...

HTML 初心者のためのベストプラクティス 15 選

HTML 初心者向けのベストプラクティスを 30 個紹介します。 1. タグを閉じたままにする過去に...

Office ファイルのオンライン プレビュー用の Vue サンプル コード

最近、電子アーカイブに取り組んでおり、バックエンドではファイルの Huawei Cloud OSS ...

Vue3.0 エラーの解決策: モジュール 'worker_threads' が見つかりません

vue3.0 への最初の試みを記録します。プロジェクトを開始したときに、「モジュール 'wo...

JavaScriptのURLオブジェクトとは何かについて話しましょう

目次概要ハッシュプロパティホストプロパティホスト名属性Href属性起源のプロパティユーザー名とパスワ...

Vue パッケージアップロードサーバー更新 404 問題に対する 2 つの解決策

1: nginxサーバーソリューション、.conf構成ファイルを変更する解決策は2つある1: 位置 ...

ウェブ音楽プレーヤーを実現する js

この記事では、参考までに簡単なHTMLと音楽プレーヤーの制作コードを紹介します。具体的な内容は以下の...

MySQL の一般的なツール例の概要 (推奨)

序文この記事では主にMySQLでよく使われるツールに関する関連コンテンツを紹介し、皆さんの参考と学習...

Linux CDの意味と使い方

Linux CD とはどういう意味ですか? Linux では、cd はディレクトリの変更を意味します...

vue シンプルメモ帳開発の詳しい説明

この記事では、参考までにEasy Notepadを実装するためのVueの具体的なコードを紹介します。...

WeChat ミニプログラム 宝くじ番号ジェネレーター

この記事では、WeChatアプレットの宝くじ番号ジェネレータの具体的なコードを参考までに紹介します。...

Linux 継続的インテグレーションで Maven を自動的にインストールする方法

Mavenパッケージを解凍する tar xf apache-maven-3.5.4-bin.tar....

React 星評価コンポーネントの実装

要件は、製品の評価データを渡すことであり、ページには対応する星の数が表示されます。 1. 異なる評価...