最近フロントエンドの面接の準備をしていますが、この問題は基本的に必ず出る問題です。少しネットで資料を調べて、自分で整理しました。
全体の流れは以下の通りです:
- URL を入力
- DNS 解析
- TCP 接続を確立
- HTTP リクエストを送信
- サーバーの恒久的リダイレクト
- サーバーがリクエストを処理し、HTTP レスポンスを返す
- ブラウザが HTML を表示
- リンク終了
URL を入力#
URL の日本語名は統一資源定位符で、リソースの位置とアクセス方法を得るために使用されます。
その構成は:プロトコル:// ホスト名:ポート番号 / パス /; パラメータ?クエリ #情報フラグメント
DNS 解析#
DNS(ドメインネームシステム)は、インターネット上でドメイン名と IP アドレスを相互にマッピングする分散型データベースであり、ホスト名に対応する IP アドレスを得るプロセスをドメイン名解析と呼びます。
DNS 解析のプロセスは、実際にはどのマシンに必要なリソースがあるかを探すためのもので、入力された URL を IP アドレスに変換する翻訳者の役割を果たします。
以下は DNS の検索順序です:
- ブラウザキャッシュ:ブラウザキャッシュからアクセス記録を読み取る
- オペレーティングシステムキャッシュ:システムの実行メモリ内のキャッシュを探す
- host ファイル:ローカルハードディスクの host ファイルを探す
- ルーターキャッシュ:一部のルーターはアクセスしたドメイン名をキャッシュする
- ISP(インターネットサービスプロバイダー)DNS キャッシュ:ローカルで見つからない場合、ISP は現在のサーバーのキャッシュを探す
- ルート DNS サーバー:ルートドメインがリクエストを受け取り、どのサーバーが管理しているかを判断し、リクエスト者にトップレベル DNS サーバーの IP を返します。
検索が完了した後、ローカル DNS サーバーはドメインの解析サーバーにリクエストを発起し、ローカルサーバーは IP アドレスをコンピュータに返し、対応関係をキャッシュに保存します。
拡張:
DNS のクエリ方法:
- 再帰的:ローカル DNS サーバーが他の DNS サーバーにクエリを行う(一般的にはまずそのドメインのルートドメインサーバーにクエリし、次に一段ずつ下にクエリする)、結果がローカル DNS サーバーに返され、その後クライアントに返されます。
- 反復的:ローカル DNS サーバーがそのドメイン名を解決できる他の DNS サーバーの IP アドレスをクライアント DNS プログラムに渡し、そのプログラムがこれらの DNS サーバーにクエリを行います(ローカル DNS サーバーがクライアントの DNS クエリに答えられない場合に使用されます)。
DNS 最適化方法:
- DNS キャッシュ
- DNS 負荷分散
- なぜ必要か:毎回リクエストされるリソースが同じマシンにある場合、マシンが耐えられずにクラッシュする可能性があります。
- 原理:1 つのホスト名に複数の IP アドレスを設定し、クエリに対する応答時に DNS ファイルに記録された IP アドレスを順番に返すことで、アクセスを異なるマシンに導きます。
TCP 接続を確立#
IP アドレスを取得した後は、3 回のハンドシェイクを通じて TCP 接続を確立します。
- 最初のハンドシェイク:クライアントが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 付きと www なしのアドレスのアクセスが同じウェブサイトのランキングにまとめられ、ウェブサイトの検索リンクのランキングが下がることはありません。
- 異なるアドレスを使用すると、キャッシュの良好性が悪化します。1 つのページに複数の名前がある場合、キャッシュに複数回現れる可能性があります。
サーバーがリクエストを処理し、HTTP メッセージを返す#
バックエンドは固定ポートから TCP メッセージを受け取った後、TCP を処理し、HTTP プロトコルを解析し、メッセージ形式に従って HTTP Request オブジェクトにさらに封装し、上位で使用します。
HTTP レスポンスは 4 つの部分から構成されています:
- ステータス行:プロトコルバージョン、ステータスコード、ステータス説明
- レスポンスヘッダー:キーと値のペアで構成され、各行に 1 対あり、「:」で区切られます
- 空行:リクエストデータを分割
- レスポンス本文
拡張:
大きなウェブサイトでは、リクエストをリバースプロキシに送信し、同じアプリケーションを複数のサーバーにデプロイし、大量のユーザーリクエストを複数のマシンに分配して処理します。
つまり、クライアントが最初に Nginx にリクエストし、Nginx がアプリケーションサーバーにリクエストし、最終的に結果をクライアントに返します。
ブラウザが HTML を表示#
ブラウザが HTML を表示するのは、解析とレンダリングを同時に行うプロセスで、大まかな流れは以下の通りです:
- HTML ファイルを解析して DOM ツリーを構築
- CSS ファイルを解析してレンダーツリーを構築
- ブラウザがレンダーツリーのレイアウトを開始し、それを画面に描画します
拡張:
reflow(回流)と repaint(再描画)について:
- DOM ノード内の各要素はボックスモデルの形式で存在し、ブラウザがその位置、サイズなどの属性を計算するプロセスを reflow と呼びます。
- これらの属性が確定した後、ブラウザはコンテンツの描画を開始し、この描画プロセスを repaint と呼びます。
reflow と repaint はページが最初に読み込まれるときに必ず経験しますが、これらのプロセスは非常に性能を消費するため、できるだけ減らすべきです。
js 解析および実行メカニズム:
解析プロセス中に JS ファイルに遭遇すると、HTML 文書はレンダリングスレッドを一時停止し、js ファイルの読み込みと解析が完了するのを待ちます(js は DOM を変更する可能性があるため、例えば document.write など)。そのため、通常 js コードは html の末尾に配置されます。
js 解析はブラウザ内の js 解析エンジンによって行われ、js は単一スレッドで実行されますが、IO の読み書きなどのタスクは時間がかかるため、後ろにあるタスクを先に実行できるメカニズムが必要です。つまり、同期タスクと非同期タスクです。
js の実行メカニズムは主スレッド + タスクキューと見なすことができます。
同期タスクは主スレッド上のタスクで、主スレッド上にスタックを形成します;
非同期タスクはタスクキュー内のタスクで、実行結果が得られるとタスクキューにイベントが配置されます;
スクリプトは最初にスタックを実行し、その後タスクキューからイベントを取り出し、その中のタスクを実行します。
このプロセスは不断にループし、イベントループとも呼ばれます。
リンク終了#
現在、ページはリクエストの時間を最適化するために、一般的に TCP 接続を持続させていますが、TCP 接続が切断されるタイミングは現在のページが閉じられるときです。
次に、TCP 接続を切断するための 4 回のハンドシェイクがあります:
- ホストが FIN を送信し、ホストはFIN_WAIT_1状態に入ります。
- サーバーが FIN を受け取った後、ホストに ACK を送信し、確認番号は受信番号 + 1 で、サーバーはCLOSE_WAIT状態に入ります。
- サーバーがデータ送信を終了するために FIN メッセージを送信し、LAST_ACK状態に入ります。
- ホストが FIN を受け取った後、TIME_WAIT 状態に入り、その後サーバーに ACK を送信し、サーバーが自分の ACK メッセージを受け取った後にCLOSED状態に入ります。