HTTP/2の概要

Pocket

HTTPの歴史

HTTPの初めのバージョンは、Webサーバとともに誕生したHTTP/0.9です。
HTTP/0.9では、ほんとうに必要な機能しか持っておらず、リクエスト内容もメソッドと取得先のパスのみで、そのメソッドもGETしかありませんでした。
また、レスポンスのヘッダの規定もなくシンプルにドキュメントを返すのみのメッセージ構造でした。

そしてHTTP/1.1までのバージョンアップによって現在のようなWebシステムにおいて必要不可欠な機能が実装されるまでにいたっています。
また、セキュリティ面の要請からSSL2.0がNetscape Navigator1.1に実装されたのが1994年です。SSLは、TCP/IPプロトコルスタックのトランスポート層の上にセッション層を追加する形で登場し、HTTPをはじめとするアプリケーション層すべてのプロトコルで恩恵を受けることができました。

バージョン 主な追加機能
HTTP/0.9 ページの取得
HTTP/1.0 ヘッダ、フォーム送信、Cookie
HTTP/1.1 バーチェルホスト、キャッシュ

そして、2015年に約18年ぶりのバージョンアップ版であるHTTP/2が登場しました。
HTTP/2では、その以前のバージョンでプロトコルスタックの問題点などを解決し、現在では、ブラウザ側とWebサーバ側での対応が急速に広まっています。

HTTPの欠点

現在のWebサイトでは、ひとつのページに多くのリソースを必要とすることがほとんどです。
しかしHTTPでは、ひとつのリクエストが完了するまで、次のリクエストを送ることができません。
その制約を回避するために、ほとんどのブラウザは1ドメインへの複数同時接続を行うことで通信の多重化を図っています。

HTTPパイプライン

このような制約のなか、HTTP/1.1では、前回のリクエストの完了を待たなくてもいいHTTPパイプラインというものがありました。

HTTPパイプラインを使うとクライアント側は前回のリクエストのレスポンスが返ってくる前によゆうがあれば、次のリクエストを送ることができるのです。しかし、サーバー側はクライアントからのリクエストの順番通りに返さなくてはいけなくて、どこかで重いリクエストが来たらその処理以降のレスポンスがブロックされてしまいます。これを「HoL(Head of Line)ブロッキング」といいます。
そして残念なことに、現在ほとんどのブラウザはHTTPパイプライン機能を全く実装していないか、デフォルトでオフにしています。これには、実装の困難さから、HTTPパイプラインを正しく実装したサーバが少ないという事情があるようです。

HTTPパイプライン

1.1時代の工夫

このようなことからHTTP/1.1での高速化は、リクエストできる数を増やすまたはリクエストする数を減らす事が重要とされてきました。
そのような工夫として、リソースの結合やCSSスプライト、画像のインライン化、ドメインシャーディングなどがあります。

リソースの結合やCSSスプライト

CSSスプライトは、サイトの読み込みを高速化する目的で使われているCSSの小技です。
具体的には「サイト内で使用するたくさんの画像(パーツ)をなるべく一枚画像にまとめて、サーバへのリクエスト回数を少なくし、CSSで座標の位置をずらして各画像を表現する」という事をやってます。リソースの結合でも、JavaScriptやCSSを複数のファイルを1つに結合し、リクエストの数を減らし、高速化する目的で使われています。

画像のインライン化

ページの表示に必要な画像はブラウザがHTMLを受け取った後に改めてリクエストする必要があります。そこで、次のように記述することで、HTML内にbase64でエンコードした画像を直接埋め込む方法があります。
これによって1回分の往復時間を減らすことができ、リクエスト数の削減にもつながります。

ドメインシャーディング

ブラウザによる同時接続は1つのドメインに対して最大6つまで(ChromeやFirefoxの場合)に制限されています。しかし、リソースの取得先を複数のドメインに分散すれば、同時接続数を増やすことができます。CDN(Content Delivery Network)は、この目的のために有効に働きます。

HTTP/2 の主な機能

ストリームの多重化

さて、HTTP/1.1ではリクエストとレスポンスの組を1つずつしか同時に送受信できないことが制限となり、プロトコルレベルでボトルネックになっていたが、HTTP/2では1つの接続上にストリームと呼ばれる仮想的な双方向シーケンスを作り、ストリームを多重化することで問題を解決しています。
 HTTP/2では、リクエストからレスポンスまでの一連のやりとりをストリームと呼び、1つのコネクション上でいくらでも並列に扱うことができます。これによってHTTPパイプラインのHoLブロッキングの問題を解決します。
ストリームはクライアント、サーバのどちらからでも開始することができます。クライアント側から開始したストリームには奇数のID、サーバ側から開始したストリームには偶数のIDをそれぞれ割り当てて、競合を防ぎます。

frame
各ストリームにおけるメッセージのやり取りはフレームという単位で行います。フレームはバイナリで定義され、各フレームは9オクテットのヘッダと、可変長のペイロードで構成されます。

ヘッダに定義されるストリームIDによって、各ストリームに処理が割り当てられます。
フレームには次のような種類があります。

Type フレームの種類 役割
0 DATA リクエスト/レスポンスのボディ部分に相当
1 HEADERS リクエスト/レスポンスのヘッダ部分に相当
2 PRIORITY ストリームの優先順位を指定(クライアントのみ送信可能)
3 RST_STREAM エラーなどの理由でストリームを終了するために用いる
4 SETTINGS 接続設定を変更する
5 PUSH_PROMISE サーバプッシュを予告します(サーバのみ送信可能)
6 PING 接続の生存状態を調べる
7 GOAWAY エラーなどの理由で接続を終了するために用いる
8 WINDOW_UPDATE ウインドウサイズを変更する
9 CONTINUATION サイズの大きなHEADERS/PUSH_PROMISEフレームの断片

ストリームの優先度

ストリームの多重化によりHoLブロッキングの問題は解決しましたが、あまり重要でないストリームが先にリソースを占有してしまい、重要なストリームが返却を待たされてしまう可能性があるのです。
これを解決するために、HTTP/2ではクライアントがPRIORITYフレームを用いて、ストリーム間に優先順位を付けることが可能となりました。

優先順位付けの方法には、「重み付け」と「依存関係」の2つがあり、あるストリームを他のストリームより優先させることや、2つのストリームをそれぞれ2:5のように重み付けを付けることなどが可能になります。
これはあくまでもリソースに余裕がないときの動作で、後続のストリームをブロックするものではありません。
ちなみに、PRIORITYフレームの送信が許されているのはクライアント側のみで、サーバー側でこの優先度を指定したり変更する方法はありません。

フロー制御

HTTP/2ではデータ転送量を制御するために、フロー制御の仕組みを用意しています。
フロー制御とはひとつのストリームがリソースを占有してしまうことで、他のストリームがブロックしてしまうことを防ぐことです。
フロー制御の仕組みはTCPでも実装されていますがHTTP/2ではストリーム毎にフロー制御が可能となっています。
この仕組みは、受信者の許容量を超えるデータが送信され、受信者のバッファが溢れること防ぐために必要です。
このような低レイヤーの役割にみえる機能も、アプリケーション層から柔軟に制御することが可能になっています。

ヘッダー圧縮

HTTP/2では従来のHTTPのコンテンツボディをgzip圧縮することに加えヘッダ部分の圧縮が可能になっています。
HTTP/2では「HPACK」と呼ばれる形式でヘッダを圧縮します。SPDYでは当初、ヘッダの圧縮にgzipを用いていましたが、後に「CRIME」と呼ばれる攻撃手法が発見されたため、これを置き換えるものとしてHPACKが考案されました。
HPACKもHTTP/2と同時にRFCが公開されています。

HPACKは次のような特徴を持っています。

  • バイナリ形式
  • キーを小文字に統一
  • 高頻度で用いられるヘッダのキーと値を組にした辞書を持つ
  • 動的に辞書を更新し、2回目以降はインデックスを用いる
  • キーや値の文字列を任意でハフマン符号化によって圧縮する
Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>