LoginSignup
9
11

More than 3 years have passed since last update.

HTTP/2: プロトコルの概要編

Posted at

この記事について

今さらだが、個人的に HTTP/2 の概要をまとめたもの。

HTTP/2 に至る背景

HTTP/1.x のコネクション管理

HTTP/1.x では、トランスポートのコネクション(通常はTCPコネクション)をどう使用/制御するか、HTTP メッセージ(リクエストとレスポンス)をどう送受信するかについて、3つのモデルがある。

  • Short-lived
    • HTTP リクエストを送信するたびに新しいコネクションを作成し、レスポンスを受信するとコネクションをクローズする
    • HTTPの初期から存在するモデル (HTTP/1.0 のデフォルト)
    • パフォーマンスやリソース消費に問題あり
  • Persistent (Keep-Alive)
    • コネクションはしばらく開いたままにして、いくつかのリクエストで再利用する。いわゆる Keep-Alive。
    • HTTP/1.1 で導入 (HTTP/1.1 のデフォルト)
    • 1つのリクエストへレスポンスが返らないと、次のリクエストを送れない (HOLが生じる)
  • Pipeline
    • 1つのコネクション上で連続してリクエストを送信できる(でも、レスポンスはリクエストされた順に従い返される)
    • HTTP/1.1 で導入
    • 結局、HOL の問題は残る
    • 実装が難しく不具合のあるサーバーやプロキシも多いことから、ほとんどのブラウザでデフォルトで無効化されてる

これらの課題に対して、ブラウザは複数のTCPコネクションを開き、並列でリクエストを送ることで対処する(同時コネクション数は、1つのドメインに対して最大で6つ)。

さらに、コネクション数を増やしたい場合は、サーバー上のリソースを複数のドメインに分散して配置するという方法がある(ドメインシャーディング)。

しかし、サーバーのリソース消費・負荷増大とさらに、コンテンツ提供者が頑張らなければいけないという問題がある。

(参考)

HTTP/1.x の問題

Webサイトのコンテンツ数増加やサイズ増大に伴い、1つのページを表示するのに大量のリクエスト送信が必要になり、上記のコネクション管理とリクエスト送信の問題によるパフォーマンスの問題が顕著になってきた。

また他にも HTTP ヘッダーのサイズが増大してきたが(特に Cookie やアクセストークンが含まれる場合)、圧縮せずそのままテキストとして送受信するため不要なネットワークトラフィックが発生するという問題も生じてきた。

HTTP/2

目標

HTTP/1.1のパフォーマンス問題に対処して、ウェブページの読み込みレイテンシを短縮すること。Google の HTTP/2 の概要によると、HTTP/2 の元となった試験的プロトコル SPDY の目標は以下。

  • ページ読み込み時間(PLT)の 50% 短縮を目指す。
  • ウェブサイト作成者によるコンテンツ変更の必要性を回避する。
  • デプロイメントの複雑さを最小限に抑え、ネットワーク インフラの変更を回避する。
  • この新しいプロトコルをオープンソース コミュニティと協力して開発する。
  • 実際のパフォーマンス データを収集し、試験的なプロトコルを検証(または無効化)する。

なお、HTTP/2 は IETF にて SPDY を元に改良を加え標準化したもの。

HTTP/2 のコア技術

コアとなる技術は フレームストリーム

  • HTTP メッセージ(リクエスト/レスポンス)をテキストではなくバイナリで送受信するための「フレーム」と呼ばれる構造と、それを処理するフレームレイヤー
  • 1つのTCPコネクション上に「ストリーム」と呼ばれる仮想的な経路を複数作成できるようになり、リクエスト/レスポンスのフレームを並列で処理する。

他にも、ヘッダー圧縮、サーバープッシュ、優先度付けなどの機能があるが順次説明。

フレーム

HTTP/2 では、1つの HTTP メッセージを複数のフレームというバイナリ構造に分解して送受信する。そして、受信側は受け取ったフレーム群をメッセージとして再構築する。後で述べるように、異なるメッセージのフレームを混在させて並行に送受信可能であり、このフレームの分解/混在/再構築が HTTP/2 で一番大事な機能。

それら処理を行うのがフレームレイヤーで Socket インターフェースとアプリケーションレイヤーの間に位置づけられる。

(HTTP/2 の概要から引用)
frame_layer.png

フレームは、HTTP/2 での通信の最小単位になり、送信するデータ種別や目的に応じて以下の種類が定義されている。

名称 タイプ 役割
DATA 0 メッセージのボディを送信
HEADERS 1 メッセージのヘッダーを送信
PRIORITY 2 ストリームの優先度を指定
RST_STREAM 3 ストリームの終了要求
SETTINGS 4 コネクションの設定とそのACK
PUSH_PROMISE 5 サーバプッシュのための事前通知
PING 6 アイドル状態のコネクションの確認
GOAWAY 7 コネクションの終了
WINDOW_UPDATE 8 フロー制御
CONTINUATION 9 フレームの継続送信

例えば、以下は GET リクエストを送る HEADERS フレームの例(Wireshark のキャプチャ)。

frame_get.PNG

ストリーム

HTTP/1.x ではサーバーに対して複数の TCP コネクションを張って並行にメッセージを送受信していたが、HTTP/2 では1のドメイン(正確に言うとオリジン)に対しては1つの TCP コネクションしか張らない

この1つの TCP コネクション上で、複数のフレームを同時に送受信するための仮想的な経路をストリームと呼ぶ。ストリームはクライアントとサーバーの双方向でデータ転送が可能。

次の特徴がある。

  • クライアントとサーバーのどちからからもストリームを開始できる
  • ストリームは整数のIDを割り当てて識別する
    • クライアントは奇数、サーバーは偶数のIDでストリームを開始する(IDの重複を防ぐため)
    • ただし、ID 0 はコネクションを制御するための特殊なストリームとして使われる
    • フレームのヘッダーにはストリームIDが格納されている
  • ストリームは同時に複数開始可能(上限は設定できる)
  • ストリームはそれぞれ独立している(他のストリームに影響を与えない)

なお、先に添付した Wireshark の画面では、ストリームID 1 で通信している(Stream Identifier: 1 となっている個所)。

ストリームと異なるリクエストのフレームが多重化され送受信されるイメージは以下のような感じ。

frame_mux.png
(HTTP/2 の概要から引用)

ストリームの優先度

クライアント側はストリームを開始する際に、依存関係(他のどのストリームに依存するか)と重みを指定することにより、サーバーにストリームを処理する際の優先度を伝えることができる。これにより、サーバーはストリームの処理に必要なリソースの割り当てを変更できる。

ロジックについてはストリームの優先順位に分かりやすく説明されてる。

ただし、この優先度は強制力はなく、サーバー側で指定した通りに処理される保証は無い。

フロー制御

ストリームごとに、受信側が受信可能なデータ量を制御する仕組み。

TCP でもフロー制御は実装されているが HTTP/2 では TCP コネクションが1つのため、それだとストリーム単位でのフロー制御ができない。ストリーム単位のフロー制御は、例えば以下の目的で使用する。

  • 特定のストリームがリソースを専有することを防ぐ
    • 大容量ファイルのダウンロードや動画を見てる場合など
  • Proxy サーバーにて、ダウンストリームとアップストリームの回線速度が大きく異なる場合に、低速な方に合わせて通信速度を制御したい

HTTP/2 のフロー制御には次の特徴がある。

  • ウィンドウサイズの更新は、WINDOW_UPDATE フレームを用いて行う
  • フロー制御は DATA フレームにのみ適用される
  • ホップ間で行われる (End-to-Endではない)

サーバープッシュ

クライアントからの1つのリクエストに対して、サーバーから複数のレスポンスを返せる機能。例えば、HTML に対して GET リクエストが来た際、サーバーは、続いてその HTML に記述されているリソース(JavaScript, CSSなど)がリクエストされることを想定できる。

そういった場合に、サーバーが想定したリソースを予めクライアントに送る仕組み。余分な通信を削減してレイテンシーを短縮する。

次の特徴がある。

  • サーバープッシュは PUSH_PROMISE フレームで事前通知してから開始される
  • もしクライアント側でリソースがキャッシュ済みなどの理由でサーバープッシュが不要な場合は、それを拒否できる

ヘッダー圧縮

HTTP/2 では、HPACK という方式でリクエストとレスポンスのヘッダーを圧縮する。HPACK は HTTP/2 のために作られた圧縮方式。

なお、ヘッダーは HEADERS フレームで送信されるが、HTTP のメソッドやパス、レスポンスのステータスコードなども HEADERS フレームで送信される。

ヘッダー圧縮には次の特徴がある。

  • 1度送信したヘッダーは基本的に再送信する必要は無く、差分のヘッダーだけを送信する
  • ハフマン符号化を用いて、転送サイズを削減する

Appendix

HTTP の歴史 概要

  • HTTP/0.9 (1991年)

    • 初版
    • メソッドはGETだけ。レスポンスは単にドキュメントの内容を返すだけで、レスポンスコードの規定もない。
    • ワンライン プロトコル
  • HTTP/1.0 (1996年)

    • RFC 1945 で規定。
    • メソッドに POST が追加
    • レスポンスにヘッダーが付くようになり、ステータスコードが返るようになった
    • コネクション管理は short-lived
  • HTTP/1.1 (1997年)

    • RFC 2068 で規定。(その後、RFC 7230~7235で規定)
    • 1.0から大幅に機能が追加される
    • HOST ヘッダが追加され、バーチャルホストがサポートされた
    • 新しく2つのコネクション管理モデル(persistent, pipeline)が導入された

(参考) HTTP の進化

9
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
11