最近、フロントエンドの面接の準備をしていますが、この問題は基本的な質問ですので、いくつかの情報をインターネットで調べて整理しました。
全体のプロセスは以下の通りです:
- URL の入力
- DNS 解決
- TCP 接続の確立
- HTTP リクエストの送信
- サーバーの永久リダイレクト
- サーバーがリクエストを処理し、HTTP レスポンスを返す
- ブラウザが HTML を表示する
- リンクの終了
URL の入力#
URL(Uniform Resource Locator)は、リソースの場所とアクセス方法を取得するために使用されます。
構成要素は次のとおりです:プロトコル:// ホスト名:ポート番号 / パス /; パラメータ?クエリ #フラグメント
DNS 解決#
DNS(Domain Name System)は、インターネット上のドメイン名と IP アドレスの相互マッピングとして機能する分散データベースです。ホスト名に対応する IP アドレスを取得するプロセスをドメイン名解決と呼びます。
DNS 解決のプロセスは、必要なリソースを持つマシンを見つけるためのものです。実際には、翻訳者の役割を果たし、入力されたウェブアドレスを IP アドレスに変換します。
以下は DNS の検索順序の一例です:
- ブラウザのキャッシュ:ブラウザのキャッシュからアクセス履歴を読み取る
- オペレーティングシステムのキャッシュ:システムのメモリ内のキャッシュを検索する
- ホストファイル:ローカルディスクのホストファイルを検索する
- ルーターのキャッシュ:一部のルーターはアクセスしたドメイン名をキャッシュする
- ISP(Internet Service Provider)の DNS キャッシュ:ローカルで見つからない場合、ISP は現在のサーバーのキャッシュで検索する
- ルート DNS サーバー:ルートドメインはリクエストを受け取り、どのサーバーが管理しているかを判断し、リクエスト元にトップレベル DNS サーバーの IP を返します。
検索が完了した後、ローカル DNS サーバーはドメイン名の解決サーバーにリクエストを送信し、サーバーは IP アドレスをコンピュータに返し、関連付けをキャッシュに保存します。
拡張:
DNS のクエリ方法:
- 逐次:ローカル DNS サーバーが他の DNS サーバーにクエリを送信します(通常、ドメインのルートサーバーに最初にクエリを送信し、次に階層ごとにクエリを送信します)。結果はローカル DNS サーバーに返され、それからクライアントに返されます。
- 反復:ローカル DNS サーバーは、ドメイン名を解決できる他の DNS サーバーの IP アドレスをクライアント DNS プログラムに提供し、そのプログラムがこれらの DNS サーバーにクエリを送信します(ローカル DNS サーバーがクライアントの DNS クエリに応答できない場合に使用されます)。
DNS の最適化方法:
- DNS キャッシュ
- DNS ロードバランシング
- なぜ必要か:リクエストされるリソースが同じサーバーにある場合、サーバーが負荷を処理しきれずにダウンする可能性があります。
- 原理:ホスト名に複数の IP アドレスを設定し、DNS ファイルに記録された IP アドレスの順序に従って異なる結果を返すことで、アクセスを異なるマシンに誘導します。
TCP 接続の確立#
IP アドレスを取得したら、3 ウェイハンドシェイクを使用して TCP 接続を確立します。
- 第 1 のハンドシェイク:クライアントはSYN(同期シーケンス番号)パケットをサーバーに送信し、SYN_SENT状態に入り、サーバーの確認を待ちます。
- 第 2 のハンドシェイク:サーバーは SYN パケットを受け取り、確認を行い、自身も SYN パケットを送信します(SYN+ACK パケット)。サーバーはSYN_RECE状態に入ります。
- 第 3 のハンドシェイク:クライアントはサーバーからの SYN+ACK パケットを受け取り、確認パケット ACK をサーバーに送信します。送信が完了すると、クライアントとサーバーはESTABLISHED状態に入ります。
拡張:
なぜ 3 ウェイハンドシェイクが必要なのか:無効な接続要求パケットがサーバーに突然送信されることを防ぐためです。
HTTP リクエストの送信#
TCP 接続が確立した後、クライアントは HTTP リクエストを送信します。HTTP リクエストには 3 つの部分があります:
- リクエスト行:リクエストメソッド + URL + プロトコル / バージョン
- リクエストヘッダー:リクエストの追加情報とクライアント自身の情報を伝える
- リクエストボディ:送信するデータ
サーバーの永久リダイレクト#
サーバーはブラウザに 301 永久リダイレクトレスポンスを返します。たとえば、**http://google.com/** にアクセスすると、自動的に **http://www.google.com/** にリダイレクトされます。
目的:
- これにより、www を含むアドレスと含まないアドレスが同じウェブサイトのランキングに含まれるようになります。ウェブサイトの検索リンクのランキングが低下することはありません。
- 異なるアドレスを使用すると、キャッシュの効果が低下し、1 つのページに複数の名前がある場合、キャッシュに複数回表示される可能性があります。
サーバーがリクエストを処理し、HTTP レスポンスを返す#
バックエンドは固定ポートから TCP パケットを受信し、TCP を処理し、HTTP プロトコルを解析し、パケットを HTTP リクエストオブジェクトにさらにカプセル化して上位レイヤーに提供します。
HTTP レスポンスは 4 つの部分で構成されています:
- ステータス行:プロトコルのバージョン、ステータスコード、ステータスの説明
- レスポンスヘッダー:キーと値のペアで構成され、1 行に 1 つのペアがあり、コロンで区切られています
- 空行:リクエストデータを区切る
- レスポンスボディ
拡張:
大きなウェブサイトでは、リクエストはリバースプロキシに送信され、同じアプリケーションを複数のサーバーにデプロイし、多数のユーザーリクエストを複数のマシンに分散させます。
つまり、クライアントはまず Nginx にリクエストを送信し、Nginx がアプリケーションサーバーにリクエストを送信し、最終的に結果をクライアントに返します。
ブラウザが HTML を表示する#
ブラウザで HTML を表示するには、解析とレンダリングが同時に行われます。大まかなプロセスは次のとおりです:
- HTML ファイルを解析して DOM ツリーを構築する
- CSS ファイルを解析してレンダリングツリーを構築する
- ブラウザはレイアウトを開始し、レンダリングツリーを画面に描画します
拡張:
リフロー(再レンダリング)とリペイント(再描画)について:
- DOM ノードの各要素はボックスモデルとして存在し、ブラウザは位置やサイズなどの属性を計算するプロセスをリフローと呼びます。
- これらの属性が確定した後、ブラウザはコンテンツを描画するプロセスをリペイントと呼びます。
リフローとリペイントはページの初回読み込み時に必ず行われる必要がありますが、これらのプロセスは非常にパフォーマンスが低下するため、できるだけ減らす必要があります。
JS の解析と実行メカニズム:
解析中に JS ファイルに遭遇すると、HTML ドキュメントはレンダリングスレッドを一時停止し、JS ファイルの読み込みと解析が完了するのを待ちます(JS は DOM を変更する可能性があるため、例えば document.write)ので、通常は JS コードを HTML の末尾に配置します。
JS の解析はブラウザの JS 解析エンジンによって行われ、JS はシングルスレッドで実行されますが、I/O 読み書きなどのタスクは時間がかかるため、後に実行するタスクを先に実行するメカニズムが必要です。これが同期タスクと非同期タスクです。
JS の実行メカニズムは、メインスレッド + タスクキューと見なすことができます。
同期タスクはメインスレッド上にあるタスクで、メインスレッド上にスタックを形成します。
非同期タスクはタスクキューに配置されるタスクで、実行結果が得られるとイベントがタスクキューに配置されます。
このプロセスは繰り返され、イベントループとも呼ばれます。
リンクの終了#
現在、ページはリクエストの処理時間を最適化するために、通常は TCP 接続を維持し続けます。TCP 接続が切断されるのは、現在のページが閉じられたときです。
次に、TCP 接続を切断するための 4 ウェイハンドシェイクが行われます:
- ホストは FIN を送信し、FIN_WAIT_1状態に入ります。
- サーバーは FIN を受け取り、ACK をホストに送信し、確認番号は受信番号 + 1 です。サーバーはCLOSE_WAIT状態に入ります。
- サーバーは FIN パケットを送信してデータ転送を終了し、LAST_ACK状態に入ります。
- ホストは FIN を受け取り、TIME_WAIT状態に入り、その後 ACK をサーバーに送信し、サーバーが自分の ACK パケットを受け取ったことを確認した後、CLOSED状態に入ります。