HTTP/3はどうやってWebを加速するか? TCP、TLS、HTTP/2の問題とHTTP/3での解決策~Fastly奥氏が解説(前編)
Webの世界では新しいHTTPの標準として「HTTP/3」の策定が進み、現在最終段階にあります。このHTTP/3はこれまでのHTTPをどのように改善し、高速化を実現していくのでしょうか。
2020年11月25日と26日にオンラインで開催されたFastly Japan主催のイベント「Yamagoya Traverse 2020」のセッション「Webを加速するHTTP/3」で、同社の奥一穂氏がHTTP/3の解説を行っています。
奥氏はHTTP/3に対応したHTTPサーバ「H2O」の開発を行うだけでなく、IETFでHTTP/3の標準策定にも関わるなど、日本においてもっともHTTP/3に詳しい人の一人であるといえます。
本記事では奥氏のセッションをダイジェストで紹介します。
Webを加速するHTTP/3
奥一穂氏。私はPrincipal OSSエンジニアとして、Fastlyで使っているオープンソースのHTTPサーバである「H2O」の開発に携わっています。
また、H2Oが使っているTLSスタックの「picotls」、QUICスタック「quicly」の開発者として開発をリードしています。
ほかにインターネットプロトコルの標準規格の著者としてRFC8297の規格の著者であったり、Extensible priorities、これは後ほどお話します。そしてTLSのESNI拡張の共著者も務めております。
HTTP/3とはHTTPの新しいバージョン
HTTP/3とは何でしょう。HTTPの新しいバージョンです。
1996年にHTTP/1.0が、2015年にHTTP/2が策定されて以来、3つ目のメジャーバージョンアップになります。
どこがメジャーバージョンアップなのか、それは暗号化された通信プロトコル「QUIC」の上で動作するという点です。
これまでのHTTP/1、そしてHTTP/2がTCPとTLSの上で動いていたのとは違うというわけです。
HTTP/2やTLS/1.3による高速化
ここでWebを高速化する手法について整理したいと思います。
みなさんは、アプリケーションレベルでminifyや結合などをしていると思います。
HTTPレベルでは、Fastlyのような企業が提供するキャッシュを使ったり、HTTP/2のような技術が存在します。
HTTP/2ではリクエストの多重化と優先度制御を行うことで、多数のリソースをHTTP/1よりも高速にダウンロードすることができるようになりました。
左側の動画がHTTP/1で右側がHTTP/2ですね。こういう動画が懐かしいなと思う人もいらっしゃるかと思います。
HTTP/2の標準策定から5年を経まして、HTTP/2は広く使われるようになりました。現在ではHTTPリクエストの約7割がHTTP/2を利用して送受信されています。
2018年にはTLS 1.3が制定されました。
TLS 1.2では接続確立のためにパケットが3回、クライアントとサーバのあいだを往復する必要がありました。
最初の1回でSYNを交換してTCPの接続を確認。次の2回目でTLSのハンドシェイクメッセージや証明書などパラメータの交換。そして3回目で暗号の仕様を切り替えた後でHTTPリクエストを投げます。
これに対しTLS1.3では、暗号化プロトコルへの切り替えを行うFinishedメッセージを送信するタイミングを変更することで、接続確立にかかるラウンドトリップを3回から2回に減らしました。
さらに再接続の場合は前回使用した暗号を覚えておくことで、TCPの接続確立直後にHTTPリクエストを送信できるようになりました。
ここでさらにTCPの接続確立、つまりSYNの交換とTLSのハンドシェイクを一体化できればもっと高速化できるんですけどね。(笑)
それはさておき。
これが昨年発表された、TLS 1.3の導入による効果測定です。Appleさんが自社のデバイスを利用しているユーザーのメトリクスを集めたもので、青がTLS 1.2、緑がTLS 1.3を使った接続での、接続確立にかかる時間の分布を表したものです。
緑、つまりTLS 1.3の方が鋭いピークが左側にあることが分かります。それだけ接続確立にかかる時間が減少したわけです。
ちなみにこのデータが発表された昨年時点でTLS接続全体の2割がTLS 1.3、HTTP/2に限ると4割がTLS 1.3を使っていたとのことです。
一方で、すべてが完璧にうまくいっていたわけではありません。こちらは弊社が2016年に発表したHTTP/1とHTTP/2のパフォーマンスとパケットロス耐性の関係を表した表です。
人工的に作ったランダムなパケットロスが0%の場合、2%の場合、それぞれについてHTTP/1が速かったか、HTTP/2が速かったかを表しています。
緑がHTTP/2が速かったケース、赤はHTTP/1が速かったケース、黄色は引き分けです。
2%のランダムなパケットロスというのは必ずしも現実のネットワーク環境に即した条件ではないのですが、パケットロスへの耐性という点でHTTP/2よりHTTP/1の方が優れていた、ということは言えるでしょう。
TCPとTLSに起因する問題と、HTTP/2自体に起因する問題
なぜそういうことが起こるのか。HTTP/2の問題点を整理したいと思います。
HTTP/2が使うトランスポート、つまりTCPとTLSに起因する問題が「ヘッドオブラインブロッキング」です。
ヘッドオブラインブロッキングとは、先行するパケットがパケットロスした場合に、後続のパケットが届いても使うことができないという状況を指しています。
TCPそしてTLSは単一のバイトストリームを使ってデータを順番に送っていく仕組みなので、先行するパケットが欠落すると、後続のパケットに、たとえ異なるレスポンスが含まれていても使えないのです。
第二の問題が、接続確立にかかるまでの時間です。
さきほど、本当はTCPとTLSのハンドシェイクが同時にできればいいのに、という話をしました。
なぜ同時にできないのか。それがOssification(硬直化)と呼ばれる問題です。
TCPは平文のプロトコルなので、ファイアウォール等がTCPの通信内容を監視しています。ここでTCPとTLSのハンドシェイクを同時に行うような、改良されたTCPプロトコルを使うと、このファイアウォールがびっくりするんですね。なんだこの通信は、怪しい、となって接続を切ってしまうのです。
そのような問題があるのでTCPの改良が難しいというのが現実です。
改良の難しいもう一つの理由が、TCPがカーネル、つまりOSが提供する機能であるという点です。
新しいプロトコル拡張を作っても、その機能に対応した新しいバージョンのOSにユーザーがアップグレードするまで待たないといけないんですね。
新しいプロトコル拡張をブロックするようなファイアウォールがあるかどうかは実際にそのプロトコルを使ってみないと分からないのですが、その確認に、ユーザーがOSをアップグレードするまで数年もかかるようでは、なかなかトライ&エラーでプロトコルの改良をすすめることはできないわけです。
HTTP/2自体も問題を抱えています。
第一が、優先度制御の実装品質。第二がサーバプッシュです。
HTTP/2の問題についてはのちほど詳しく話しますが、ともかくこれらのうち、トランスポートレベルの問題を解決するのがQUICになります。
QUICの特徴
QUICの特徴について整理したいと思います。
第一が暗号化されたトランスポート層プロトコルであると言うことです。TCPとTLSが提供してきた、両者の機能をQUICが提供します。また、先に挙げた4つの問題を解決します。
まず、ヘッドオブラインブロッキングの発生を防止します。また、トランスポート層のハンドシェイクと暗号のハンドシェイクを同時に行うことで、接続確立までの時間を最小化します。
そしてパケットに流すほとんどすべての情報を暗号化することで、ファイアウォール等によるプロトコルの硬直化を回避します。
また、TCPと異なりプロトコルスタックがアプリケーションの一部として実装されるので、アップグレードが容易になります。
たとえばユーザーがWebブラウザをアップグレードすれば通信プロトコルもアップグレードされる、そんな形になるんですね。
そしてQUICを使うHTTPがHTTP/3です。
QUICプロトコルのレイヤ構造
QUICプロトコルのレイヤ構造を図にするとこんな感じです。TLSは従来、暗号化とハンドシェイクを担当していましたが、QUICではハンドシェイクのためだけに使います。暗号化はQUICの担当です。
HTTP/2が担当していた多重化もQUICの担当になって、HTTP/3はHTTP/2と比べるとシンプルなものになっています。
そんなQUICとHTTP/3ですが、現在プロトコル設計が完了し、最終レビューにかかっている状態です。
またGoogle Chromeのユーザーのうち、25%以上でHTTP/3が自動的に使われるようになっています。これはサーバが対応している場合の話ですが、そのようになっています。
また、Facebookは独自のサーバとクライアントを持っているわけですが、それらでHTTP/3を有効にした結果、彼らのインターネットトラフィックの75%がHTTP/3になっているということです。
HTTP/2の問題点はQUICで解消されるか?
問題点の整理に戻りましょう。トランスポート層の諸問題はQUICで解消しました。
では、HTTP/2固有の問題はどうでしょうか。
HTTP/2の優先度制御は、クライアントが優先度を表現する木、つまりツリーを作り、サーバがこの木に指定されたような順序でレスポンスを配信するという形をとります。
右下の図はFirefoxが使う、優先順位を表現する木ですが、レスポンスの配信順序や使うバンド幅の割合といったものを表現しています。
こちらはGoogle Chromeが使う木です。1本の鎖のようになっていて、順番に1つずつレスポンスを返してほしいと伝えている感じです。
このようにHTTP/2の優先度表現は非常に表現力に富んでいるのですが、複雑です。その結果、正しく実装していないサーバが多いということになってしまいました。
また、クライアントによって木の使い方が異なる結果、サーバ側で優先度を変更しにくいという問題も抱えています。
例えば、サーバ側で、この画像は重要だからほかの画像よりは先に、でもCSSよりは後に送りたい、となったとしても、木の形がブラウザごとに異なるため、木の形を上手に変更することがとても難しいのですね。
HTTP/3では優先度制御を再設計
結局、HTTP/3の標準化と同時に新しい優先度制御手法を取り入れることになりました。
この新しい手法が「Extensible Priorities」と呼ばれるものです。
Extensible Prioritiesは、HTTPのバージョンに関係なく動作するように設計されていて、HTTP/3で使用されると同時に、HTTP/2についても今後Extensible Prioritiesへの移行が想定されています。
Extensible Prioritiesでは、優先度をHTTPリクエストヘッダで指定します。この下の例にあるプライオリティヘッドですね。
HTTP/2および3については、フレームと言うHTTP/2もしくはHTTP/3固有の仕組みを使って優先度を指定することができます。
これを使って転送中の優先度変更も行います。
ちなみにこのプライオリティヘッダにあるuがUrgency、優先度を表すパラメータです。0から7の8段階で、バックグラウンドが一番大きい7です。
そして7を除いた7段階の真ん中にある3がデフォルト値になります。iはレスポンスがインクリメンタルに処理可能かどうかを表します。
例えば一部のブラウザではJPEG画像を受信しながらじわじわと表示する機能を備えていますが、このようなブラウザはJPEGをリクエストする際にiフラグをセットします。
また、サーバがプライオリティレスポンスヘッダを指定することで、優先度を変更することも可能です。
ここで例を見て見ましょう。
最初はファイルのバックグラウンドダウンロードだけが走っています。優先度はバックグラウンドの7ですね。
ブラウザがWebページを開こうとします。優先度はデフォルトの3です。
HTMLを受信するにつれてブラウザはCSS、JavaScriptや画像をリクエストします。CSSやJavaScriptはHTMLをレンダリングするのに必要なので、HTMLよりも優先度が高く設定されています。u=1ですね。
画像はHTMLとバックグラウンドの中間の優先度5に設定されています。画像はじわじわと表示でいるのでi=1、CSSやJavaScriptは全部ダウンロードしないと適用できませんから、iフラグはついていません。
ここでヒーローイメージ、つまりユーザー体験に重要な画像だけ優先度を上げたいということがあるとします。その場合、オリジンはヒーローイメージのHTTPレスポンスにu=4というヘッダをつけてFastlyのエッジサーバにキャッシュさせておきます。
そうするとFastlyのエッジサーバはこの画像を投げるときにレスポンスヘッダを見て、この画像はほかの画像より優先しなくてはいけないんだと判断して、ブラウザに配信できます。
≫後編に続く~後編ではHTTP/2における「サーバプッシュ」の問題とそれに代わる「103 Early Hints」、HTTP/3は本当に速いのか、などについて解説します
関連記事
- レイテンシに負けないプロトコルとして登場したHTTP/2~奥一穂氏による「HTTPとサーバ技術の最新動向」(前編)。Developers Summit 2016
- リクエストを待たずに送信を開始する「サーバプッシュ」~奥一穂氏による「HTTPとサーバ技術の最新動向」(中編)。Developers Summit 2016
- HTTPS化もHTTP/2にすれば負荷を下げられる~奥一穂氏による「HTTPとサーバ技術の最新動向」(後編)。Developers Summit 2016
- 「HTTP/3」がHTTP-over-QUICの新名称に。IETFのQUICワーキンググループとHTTPワーキンググループで合意
- QUICとHTTP/3がIETFのラストコール。RFCによる標準化が間近に
2021年5月、QUICがRFC 9000としてインターネット標準となりました。
あわせて読みたい
HTTP/3はどうやってWebを加速するか? TCP、TLS、HTTP/2の問題とHTTP/3での解決策~Fastly奥氏が解説(後編)
≪前の記事
Ruby 3.0正式版リリース。「Ruby 2の3倍速」到達、型の記述、スレッドセーフな並列処理など新機能