ブログのしゅーくりーむ

技術的なメモとかライフログとか。

HTTP/2 のコネクション再利用について確認してみる

はじめに

本記事は http2 Advent Calendar 2015 の 12 日目の記事となります。

本記事では HTTP/2 における TCP コネクション再利用とその周辺仕様を確認してみようと思います。コネクション再利用の挙動を理解することは、実際の Web サイトにおける HTTP/2 のデプロイの助けになると思われます。

HTTP/1.1, HTTP/2 での TCP コネクション管理

よく訓練された方々には既知のことかとは思いますが、 HTTP/2 において HTTP/1.1 の頃にあった性能向上の為のハックであるドメインシャーディングは好ましくないテクニックになってしまいます。 HTTP/1.1 において今日のブラウザは 1 ドメインあたり概ね 6 TCP コネクションほど張って、並列にリクエストを送るような振る舞いをします。このため Web サイトなどで参照するドメインを分割することで HTTP リクエストの並列度を向上する手法がよく取られていました。 しかしながら HTTP/2 において HTTP リクエストの処理は 1 本の TCP コネクションの上で、ストリームという論理単位で HTTP リクエストが並列処理されるようになり、このようなハックは不要になりました。むしろ参照するドメイン名が集約されていた方が、 TCP コネクションを張る回数が削減できて性能の向上に貢献できます。

これは悩ましい問題です。今日までに HTTP/1.1 を前提としたチューニングがされた Web ページの構造を HTTP/2 向けに構築し直すのは骨が折れる作業といえます。また、依然として HTTP/1.1 ユーザも数多くいるわけで、現在 HTTP/2 前提の Web ページ構成にしてしまって良いのかどうかの判断も難しいといえます。 本記事ではこの課題に対する一つの対策となりうる、 HTTP/2 のコネクション再利用呼ばれる振る舞いについて記述します。

HTTP/2 におけるコネクション再利用

RFC7540 Section 9.1.1 Connection Reuse の節には下のような記述があります。

   Connections that are made to an origin server, either directly or
   through a tunnel created using the CONNECT method (Section 8.3), MAY
   be reused for requests with multiple different URI authority
   components.  A connection can be reused as long as the origin server
   is authoritative (Section 10.1).  For TCP connections without TLS,
   this depends on the host having resolved to the same IP address.

   For "https" resources, connection reuse additionally depends on
   having a certificate that is valid for the host in the URI.  The
   certificate presented by the server MUST satisfy any checks that the
   client would perform when forming a new TLS connection for the host
   in the URI.

https://tools.ietf.org/html/rfc7540#section-9.1.1

HTTP/2 において、 オリジンが authoritative であれば既に張られているコネクションを再利用できるのです。 再利用できるかどうかの基準については、再利用しようとしているコネクションにおいて、リクエスト対象のドメイン名がサーバ証明書の subjectAltName にマッチするかどうかで判断します。 例えば、まず https://a.example.com/ にリクエストを投げるために a.example.com にコネクションを張った際にサーバ証明書の subjectAltName に "*.example.com" が含まれている場合、続いて https://b.example.com/ にリクエストを投げる際に以前使ったコネクションを再利用することができます。

f:id:syu_cream:20151211224939p:plain

この仕様により、冒頭で述べた HTTP/1.1 の既存のハックの改修を行わずに HTTP/2 のコネクション集約の恩恵を受けられる可能性が出てきます。 これを利用して、例えば利用ドメインと証明書を集約して持つエッジサーバなどを構築し、 TCP コネクションを束ねて受けてしまうなどの対応が行えます。

Chrome における コネクション再利用

Google Chrome は既にコネクション再利用のロジックが入っているようです。いくつかの Google の Web ページを閲覧した後 HTTP/2 セッションのステータスを chrome://net-internals/#http2 で確認したところ、下記のようになっていました。

f:id:syu_cream:20151211214255p:plain

いくつかのドメイン名の HTTP/2 セッションが集約されていることがわかります。 ただし、 サーバ証明書の subjectAltName としては *.google.com が含まれているのですが、これにマッチして集約されることが期待されるいくつかのセッションが分割されています。

コネクション再利用の落とし穴

このコネクション再利用ですが、サーバとしては望まないコネクションの再利用が行われる可能性があります。 RFC7540 で記述されている通り SNI を元にリクエスト転送先オリジンサーバを決定している HTTP/2 プロキシサーバが存在するかもしれませんし、サーバ証明書としてはワイルドカードの subjectAltName を持っているものの、実際にはレスポンスを返せない場合があります。 このような場合には、サーバはクライアントに対し 421(Misdirected Request) を送って通知することが可能です。

f:id:syu_cream:20151211224943p:plain

ただし、クライアントがこの挙動を考慮して「コネクションを再利用してリクエストを投げてみて 421 が返されてから、再度コネクションを張り直してリクエストを投げ直す」ような挙動をした場合、もちろんレイテンシが向上してしまいます。 この問題を解決するために、 Origin フレーム という HTTP/2 の拡張フレームの Internet-Draft も存在します。 Origin フレームを用いることでサーバ証明書としては集約されてしまうオリジンがあったとしても、サーバが実際に authoritative なオリジンを指定することができ、これにより上記のレイテンシ向上問題を回避することができます。 (Origin フレームは拡張仕様であるためサーバ、クライアント両者が対応していなければならず、また大量のオリジンに対して authoritative であるサーバは Origin フレームにそれらを列挙し送信すると、その送信自体のコストが大きくなってしまいそうな欠点はありますが。。。)

f:id:syu_cream:20151211224947p:plain

おわりに

HTTP/2 のコネクション再利用が期待できると、現在の Web にデプロイした際に HTTP/2 の恩恵が受け易くなると思われます。 ただし現状クライアントの実装と周辺仕様の充実、さらに実際に運用した上で得られた知見が必要である状態だと思われます。