Unity でアセットバンドルのダウンロードで使用しているのは HTTP/1.1 を使用していることが多いと思います。
我々は今回、Unity から HTTP/2 で通信を行うモジュールを作成。
これを、セガ・インタラクティブのスマートフォン向けタイトル 「リーグ・オブ・ワンダーランド」のアセットバンドルのダウンロード
で主に使用中。
HTTP/1.1 と HTTP/2 を使用できるAPIセットはこのようになっています。
有名なもののみを列挙
測定の条件
データセットはリーグ・オブ・ワンダーランドのベータテストのときのものを使用。
約1800ファイル、 285MBのデータ。
AWS CloudFront から、WiFi 環境の iPhoneX でダウンロード完了の時間を計測。
ダウンロード時間の結果
• HTTP/2 では処理完了まで、15秒 (3コネクション)
• HTTP/1.1 3コネクション 127秒
• HTTP/1.1 6コネクション 100秒
ちなみに Android 端末でも同様の傾向。
HTTP/2 とはどのようなものなのか、概要について説明していきます。
HTTP/2 は 2015年に標準化を完了し公開。
いくつかの例外もありますが、 HTTP/1.1 の仕様を基本的に継承
GET/POST などのメソッドは同じ。ヘッダ、ボディという構造を取っているところは同じ。
HTTP/2 の前身は Google の SPDY というプロトコル
HTTP/2 の現在は普及率は https://w3techs.com/technologies/details/ce-http2/all/all の結果による。
HTTP/2 最大の特徴ともいえるのが Stream (ストリーム) です。
ストリームとは TCP 1接続の中で仮想的な双方向の通信路です。
図では白いパイプが TCP の接続を示し、青色のラインがストリームを表現しています。
HTTP/2では各HTTP のリクエストとレスポンスはこのストリームの中を往復します。
ストリームは多重化ができます。
この図のように、TCP1接続の中で複数のストリームの通信が可能。
HTTP/1.1 では複数接続を張って多重化への対応していることが多い。
Webブラウザだと1ドメインあたり6コネクションほど。
対して、HTTP/2 のストリームの数は 100 以上が推奨。
HTTP/1.1 では リクエスト発行、レスポンス受信の間では、別の通信データを流すことはできない。
HTTP/2 なら、その間も別のストリームが通信が可能です。つまり全二重の通信を実現。
ストリームには優先度の重み付けとしてストリームウェイトが設定可能
どのデータを優先的に送受するのかを指定できます。
各ストリームごとのフロー制御のパラメータとして使われます。
ストリームには依存関係が設定可能です
ただし、現在 HTTP/2 の優先度・依存関係に関係する仕様が見直されています。
活用する場合には、この点は注意が必要です。
HTTP/2 ではフロー制御の機構を備えています。
通信データ送受信のためのウィンドウサイズの制御を HTTP/2 が行います。
ここでのウィンドウサイズとは HTTP/2 におけるウィンドウサイズです。TCPのウィンドウサイズではありません。
ウィンドウサイズはコネクションとストリームに対して設定され、受信バッファ溢れを防ぎます。
他にも大きなファイルサイズのダウンロードで、帯域を食いつぶして他の通信を妨害しないよう制御をおこないます。
HTTP/2 ではヘッダを圧縮する HPACK が導入
• HTTP/1.1 ではヘッダはテキスト形式でやり取りされ、大きなデータとなることも。
• HPACKではハフマン符号化や、動的・静的テーブルなどの仕組みが使用されています
• アセットダウンロード以外の通信においては、このヘッダが小さくなることはメリットになるかもしれません。
HPACK の中身については少しややこしいので、より詳細な内容が気になる人は RFC 7541 を確認してみてください。
ストリームの中でやり取りされるデータ単位はフレーム。
HTTPサーバーとの通信プロトコルにおいて HTTP/1.1 ではテキストベースでしたが、HTTP/2ではバイナリベースとなりました。
通信のトランスポートとしては大きな変化がありましたが、アプリケーションレイヤから通信結果を見るとHTTP/2 と HTTP/1.1 の差は基
本的にありません。
HTTP/2 ではゲームが長期運営となった未来でも高速性を維持することが可能。
このグラフは ゲームで使用するリソース量がこの先10倍まで増えたときを想定して、計測を行ったものです。
HTTP/1.1 と比べ HTTP/2ではダウンロード待ち時間の増加が緩やかです。
内訳:
リソース量が10倍(ファイル数が10倍となり総バイト数が10倍) 。
10倍の時点で、 HTTP/2 158s、 HTTP/1.1 1309s
HTTP/1.1 で複数のコネクションを使って、ダウンロードを行っている状況を考えます。
このとき実際には左の図のようにコネクションごとにデータ転送の制御が行われるため、1つのコネクションの中でも使用している帯域は
刻々と変動しています。
しかしこの図を、右側の図のように簡略化したものを考えます。
各コネクションを色分けされたブロックで表現しています。3コネクションの例なので、色ブロックとしては3種類あります。
各ブロックはその時間において、通信を行っているかどうかを示しています。
点線で囲った部分に注目してみると、青色のブロックが消えて1つのコネクションが通信を終えたことを示しています。
1つのコネクションが通信を終えたとき、回線の使用している帯域はその分減少します。
各コネクションが通信を開始・終了する状況下では、この図の点線で囲った部分で示される領域が、回線としての帯域を有効に使えてい
ません。
ファイルサイズが小さいときはこのような状況となり、 HTTP/1.1 と TCP の性質によって帯域を使い切ることはできません。
他のアクティブな通信によって帯域を有効活用される可能性はあるが、小さなファイルサイズではこのような傾向となります。
それは、 TCPはスロースタートのプロトコルであり、各コネクションが別々にデータ転送の制御を行うためです。
少し強引な表現かもしれませんが、 TCPでは長時間接続、一括データ転送のほうが得意なため、このような結果になります。
通信帯域の課題以外にも HTTP/1.1 には Head of line Blocking (HoLB) という課題もあります。
HTTP では基本的には、リクエストとレスポンスを1組とし、レスポンスが返るまでは次のリクエストを出せません。
この図では同時接続数を3としたときの図です。4つ目のリクエストを出す前には前の処理完了を待つ必要がある。
このように後続のリクエストがブロッキングされるのを (HTTPの) Head of line Blocking と呼びます。
(HTTP/1.1にはPIPELINEという機能もありましたが、現在はあまり使われていないようです。)
HTTP/2 の場合でも先ほどと同様に簡略化したモデルで時間ごとの通信事情を考えてみます。
この図では、各ストリームが通信をしている部分を色分けしたブロックで表現しています。
縦軸は全ストリーム使用帯域の合計を 100% として、各ストリームが使用している割合を色付けしています。
1つのコネクションで多数のストリームを処理しており、コネクションを切らずにすむため、TCPレベルでは広い転送帯域の状態で通信
ができます。
HTTP/2では、複数のストリームが同時に存在し、帯域を有効活用します。
図中に示した個所では1つのストリームが通信を終えても、すぐに次のストリームが通信を開始する様子を示しています。
図中に示した個所で、新規に緑のストリームが通信を始めたときには、
帯域を適切に配分してデータを送受信します。この例ではストリームウェイトが等しく、均等割りされている様子を示しています。
先ほどの HTTP/1.1 での HoLB は HTTP/2 で解消します。
この図では、TCP接続としては白色のパイプで、各リクエスト・レスポンスは矢印で示しています。
1つのコネクションでストリームが多重化されるため、他の処理完了待ちを待機する必要がありません。
図ではリクエストとレスポンスで交互の通信となっていますが、実際にはこのようにはなりません。全二重の通信ができます。
HTTP/1.1 ではリクエスト・レスポンスのやりとりが順番に発生。
HTTP/2 ではリクエストを投げるのもレスポンスが返ってくるのも、他のストリームと並行して処理できます。
これにより HTTP/1.1では1リクエスト処理するのに1RTTの時間がかかりますが、 HTTP/2では 1RTT の時間で複数のリクエストとレス
ポンスを処理することができます。
このように待ち時間が大きく改善されることで、特に RTT の値が高い状況、特にモバイル回線では大きな影響として現れてきます。
開発した通信モジュールに 1000 ファイルのダウンロードリクエストを発行。HTTP/1.1, HTTP/2 どちらも4G 回線の環境下で3コネクショ
ンを使用しています。ダウンロード完了までの時間は、 HTTP/1.1 では40秒ほど、HTTP/2 では 4.8秒ほどでした。
HTTP/2 では多数のファイルでの並列ダウンロードが有効である様子を示す結果となりました。
(参考情報)
接続数は 3 コネクション
接続先AWSは日本リージョン
21KB x 1000 (files) ≒ 20MB
HTTP/2 にも弱点があります。
下層のトランスポートにTCPを使用しているため、 パケット欠損による TCP Head of line blocking (HoLB)が存在します。
パケロスが発生すると全Streamに影響が出ます。
TCPパケットの再送により、他のリクエスト・レスポンスの処理が遅延・ブロッキングされます
HTTP/2 の特徴を生かしつつ、HoLB を解決するものとして HTTP/3 に期待しています。
HTTP/3 ではトランスポートが QUIC プロトコルに変わります。これにより HoLB は解決します。
しかし、これらに関するものはドラフト状態なので、気になる人はチェックしてみてください。
作成したモジュール : リーグオブワンダーランドで使用されているHTTP/2のモジュール
通信の実処理 : C/C++のライブラリ
Unityで利用するためのラッパ層 : C#
本モジュールの開発開始 : 2018/6から
Windows対応 : リーグオブワンダーランドでは開発に Windows 機を用いている為
保守 : リーグオブワンダーランドのアプリ作成チームは長期的にUnityを触っているチームだったので C# の方が相性が良かった
リーグオブワンダーランド : Unity 2018 系を利用
HttpClient : .Net Framework 4.5から入ったC#の標準モジュール
Backend : HttpClient 経由で最終的に呼び出される通信モジュール
Xamarin版 HttpClient : Xamarin 経由で各 OS のネイティブ API がコールされる
【補足】
Xamarin版をUnityで使う場合 : DLL化することで呼び出すことが可能
問題点
・DLL のサイズがかなり大きくなってしまう
・DLL のシンボルと Unity 側が利用している Mono のシンボルが衝突しないような工夫も必要
【補足】
Java 版 HttpClient Java11 から正式な API として採用されたようなので今なら選択肢に入る
Jetty : Windows 版があるが Windows の Unity Editor から Java コールはしたくない
ソケットを一からたたいて HTTP/2 を実装する時間はなかったので OSS で良いものがないか調査
libcurl : CURL の内部実装に用いられている
リーグオブワンダーランドのクローズドベータテストでも libcurl 固有の大きなバグはなし
【補足】
・Windows
libcurl のパッケージに同梱されている config-win32.h ベースに作成する。
・Android : https://android.googlesource.com/platform/external/curl/
公式パッケージのビルドで使われている CURL のコンフィグがあるのでこれを流用する
・iOS : https://opensource.apple.com/source/curl/
公式パッケージのビルドで使われている CURL のコンフィグがあるのでこれを流用する
【補足】
Linux 系 OS の端末は OPEN_MAX 等の「上限の定義値」と「実測値」が異なることがある
・レアケースではある
・対策 : ストリーム数を適切な値にする等
・HTTP/2+TLS1.1 で通信できるサイトは世の中にも結構な数存在する
・CDNの中にも制限外で通信可能なものがあるのを確認している
CDN等の自身で設定を制御できないクラウドサービスを利用している場合、こうした制限に関する設定がいつ変わるかはわからない
⇒ 基本的にはこの制限を守る実装として、やむを得ない事情がある場合のみ制限を解除するような運用とする方が安全
呼び出しコスト : Windows, Android, iOS 共通で小さい
【組み込み時の注意事項】
証明書更新時に、クライアント側で保持する証明書のハッシュ値についてもきちんと更新できるような機構を入れる
リーグオブワンダーランドではAWSを利用
リーグオブワンダーランドではコネクションを 3 つ ⇒ 384 ストリームまで増やして対応
【補足】
2019/08/19 現在、 CloudFront のディストリビューション毎の 1 秒あたりのリクエスト最大数は10万
制限の引き上げにはお金がかかるのでプロジェクトの都合にあわせてコネクション数を設定しよう
・講演の最初に見せた速度比較の図 : 実は RTT が 150~200 msec 程度の海外のディストリビューションのクラウドフロントを利用
・HTTP/2 であれば海外ディストリビューションのみの運用でも高速にアセットダウンロードを実現可能
・HTTP/2 採用でユーザの利便性を上げるだけではなく、タイトルの運用コストを下げることも可能
・高パケットロス環境でダウンロード速度が逆転されるケースがある、という調査結果がある
・HTTP/1.1 を 3~6 本としている理由
HTTP/1.1 においては同時に 3 ~ 6 コネクション張って運用するのが一般的だから
【補足】
調査結果元 : 「HTTP/2: What no one is telling you」
https://www.slideshare.net/Fastly/http2-what-no-one-is-telling-you
fastly : Hooman Beheshti
・報告 : パケロス率が 2% を超えると逆転したケースがある
・すべての環境で同じ値が閾値になるわけではない
「RTT, データの数, データのサイズの分布, 帯域, 輻輳制御, QOS」等、関係する要素は非常に多岐にわたる
・対策 : HTTP.1.1 にフォールバックする機構をいれる、HTTP/2 でもコネクションを増やす
【Unite Tokyo 2019】大量のアセットも怖くない!~HTTP/2による高速な通信の実装例~
【Unite Tokyo 2019】大量のアセットも怖くない!~HTTP/2による高速な通信の実装例~

【Unite Tokyo 2019】大量のアセットも怖くない!~HTTP/2による高速な通信の実装例~