CalendarHTTP2NWはじめにこんにちは、インフラストラクチャ本部の後藤です。15日目の記事になります。前回はWebサイトをHTTP/2に対応するためにリバースプロキシを検証した記事を書かせていただきました(HTTP2を試してみる)。あれから幾つかの議論を経てHTTP/2の仕様も大分安定してきており、HTTP/2を実装したクライアントや実験的にHTTP/2を有効にしているサービスもあるので実際に試すことも出来ます。そこで今回は応用編としてHTTP/2のサーバプッシュについて、その仕組と実際に試したことについて書かせていただきます。正しい名称になります。HTTP/2概要まず、軽くHTTP/2の概要に触れておきます。HTTP/2は2012年の末頃より、HTTP/1のセマンティクスを維持したままパフォーマンスを改善する目的で議論が開始されました。Googleの考案したSPDYと言うプロトコルをベースにしており、一つのTCPコネクション上でHTTPリクエストやHTTPレスポンスをフレームというメッセージとして多重化して送受信します。メッセージを多重化するだけでなく、以下の機能を持っていますヘッダ圧縮:ハフマン符号や辞書データを用いることで、HTTPヘッダを少ないデータで表現する(h2-14でかなり簡略化されました)優先付け:クライアントがリクエストに優先度を付加することが出来るフロー制御:各通信単位でフロー制御が行えるサーバプッシュ:サーバはリクエストが無くてもレスポンスを返せるどの機能も非常に面白いのですが、今回はその中のサーバプッシュについて詳しく説明していきます。サーバプッシュの概要HTTP/1では、サーバはクライアントからのリクエストがあって初めてレスポンスを返すことが出来ました。HTTP/2では、サーバはクライアントからのリクエストが無くてもレスポンスを返すことが出来ます。HTTPのレスポンスをプッシュするというのが少々イメージが湧きづらいかと思いますが、どういう事か以下の図で説明します。クライアントはサーバにindex.htmlをリクエストします。この時リクエストされたindex.htmlはhoge.jsを読み込むhtmlだったとします。リクエストを受信したサーバはindex.htmlの中身を知っているので、次にクライアントからhoge.jsがリクエストされる事が予想できます。そのような場合にサーバプッシュを使います。サーバプッシュをすることでクライアントからの要求なしにhoge.jsのレスポンスを返すことが出来ます。することで、クライアントはhtmlをパースしてから改めてリクエストを送信するよりも早くhoge.jsを受け取れる事になり、Webページを表示するのに待たされる時間も短くなります。サーバプッシュを体感する以上の説明は概要になります。実際にサーバプッシュするサーバを構築し、どのようなメッセージがやりとりされているか見てい行きましょう。今回は、nghttp2のpythonバインドを使用し、簡単なサーバを作ってみることにします。14.04になります。install環境構築が出来たところで、サーバプッシュを体感するために簡単なサーバを作成します。リクエストが来たら、index.htmlを返し、さらにhoge.jsをプッシュする簡単なサーバです。False)にも出来る仕組みになっています。ssl=None)server.serve_forever()ちなみに使用するindex.htmlの中身は以下のようにhoge.jsを読み込むだけのものになります。Chromeのデベロッパーツールでサーバプッシュをする場合としない場合の様子を見て行きましょう。ロード時間の縦軸合ってないことに注意して下さい。Falseプッシュあり:最初のindex.htmlとともにhoge.jsも受け取っており、実際にリクエストが行われるときはキャッシュから読み込まれています。そのため、一瞬でhoge.jsを読み込めています。プッシュなし:index.htmlを受け取ってから改めてhoge.jsをリクエストしているため遅くなっています。このように、サーバプッシュを行うことでラウンドトリップ回数が減り、待ち時間が短くなっていることが分かります。サーバプッシュの中身を覗くサーバプッシュ出来たので、実際にどのようなメッセージがやりとりされているか気になる方も居ると思います。そのような人のために、どのような仕組みでサーバプッシュが実現されているか少々深堀りしてみましょう。HTTP/2のフレームレベルの話しになりますので、簡単に説明しておきます。冒頭でも触れたとおり、HTTP/2ではHTTP/1のセマンティクスを維持しておりますが、そのメッセージの送信方法はバイナリ形式のフレームで送受信されています。各フレームはストリームという単位で管理されており、HTTP/1でいう一対のHTTPリクエスト・HTTPレスポンスが一つのストリーム上で行われるイメージです。HTTPリクエストをするたびに新しいストリームが生成されます。HTTPヘッダの情報が格納されるフレーム。実際はヘッダ圧縮された形式で表現される。HTTPボディの情報が格納されるフレーム。通信する上でのパラメータを通知するフレーム。サーバプッシュをするために想定リクエストヘッダと、レスポンスのためのストリームを予約する。以上を踏まえて、サーバプッシュを行う際にどのようなフレームが送受信されているのかを説明していきます。(HTTP/2を利用する上での接続で手順は省略しています)最初のメッセージになります。このフレームはサーバプッシュとは関係なく、通常のリクエストになります。HEADERSフレームはHTTPリクエストのヘッダが格納されているフレームになります。HTTP/2ではHTTP/1で使用していたヘッダがそのまま使われていますが、一部「:」で始まる擬似ヘッダが定義されています。test-clientサーバから、サーバプッシュのためにPUSH_PROMISEフレームが送信されます。このフレームではレスポンスを送信するために使用するストリームの予約及び、想定するHTTPリクエストに関する情報が含まれています。実際は行われていませんが、どのようなリクエストに対するレスポンスなのか明示するために以下のようなヘッダ情報が付加されています(実際はヘッダ圧縮されています)。このヘッダ情報を見てクライアントは、キャッシュを行い、以下の様なリクエストを行う際はプッシュされたリソースを使うことが出来ます。hoge.js(1)でクライアントからのリクエストに対するレスポンスになります。HTTP/1.1のレスポンスヘッダはHEADERSフレームで表現されます。src="./hoge.js"></script></html>>(2)で確保されたストリームでサーバプッシュで送るデータのレスポンスヘッダ・レスポンスボディ情報が格納されています。<em>console</em>.<em>log</em>("hoge");12//hoge.jsの中身<em>console</em>.<em>log</em>("hoge");以上のようにサーバからレスポンスがプッシュされています。今回は説明しませんでしたが、フレームには制御用のフラグや、プライオリティ・パディングなども設定される場合があります。詳しく知りたい方は、Wiresharkでは既にHTTP/2に対応しています(開発版だとdraft14対応)ので、パケットをキャプチャすることでフレームのフラグやヘッダがどのように圧縮されているかなどが確認できより理解が深まりますので、是非試してみてください。実際のサイトで試す上記で簡単に試したpythonのコードを多少改変し、Proxy機能とプッシュのルール設定を出来るようにしまいた。このPush+Proxyサーバを介して、普段使用している開発環境にプロキシする構成を作りました。pathから、プッシュすべきファイルを対応付けたyamlを読み込む簡単なものです。以下のようなシンプルなyamlファイルで、「https://localhost/」にアクセスした場合に「/css/portal.css」,「/js/jquery.min.js」をpushする事を意味しています。/hoge.jsPC版GREEのトップページで、css,jsファイルをプッシュされる様子をChromeのデベロッパーツールで見てみましょう。pushありpushなし最初の実験と同様、cssとjsが一瞬でロードされていることが確認できると思います。index.htmlを中継しながら、pushの準備を開始しているわけですが、遅くなることもなくラウンドトリップ回数を減らすことができています。おわりに今回はサーバプッシュで少し遊んでみました。簡単な例ではありましたが他にも、htmlを動的に解釈しプッシュする、プライオリティを加味する、はたまたHTTPライブストリーミングと合わせるといったことも出来るでしょう。実際のサービスに導入するのには考えなければならないことも沢山あります。仕様で指定された暗号スイートのサポート、ALPNのサポート興味を持っている方がいれば是非お話したいですし、試してみたという話があれば是非情報交換させてい頂きたいです。このブログでもそういった話が出来るようにこれからも頑張っていきますので、よろしくお願いします。Japan」として報告されています。イベントも行っており、先日行われたHTTP2Conferenceも大盛況でした。定期的に勉強会も行われておりますので興味ある方は参加してみるのもいいかと思います。Studiosの久富木さんになります。