うな(。・ε・。)

Android, iOS, AppEngine まわりのめもめも

Facebook の高速ページ表示を支える BigPipe

BigPipe: Pipelining web pages for high performance

非常に面白い内容だと思うので、メモついでに紹介します。

一言でいうと

ページの枠組みと、付随するページフラグメント郡を一つのリクエストでまとめて要求・非同期受信する技術です。

具体的には、トップ画面を表示したときに、始めにトップ画面の枠組みだけをまず表示しておきながら、「フィード」「広告」「通知」「オンラインの友人」を非同期に受信し、表示していくことが出来ます。

利点

最も大きな利点は、多数のデータ取得の結果を待たなくともページの枠組みをさっさと返せることです。

でも、これだけでは「XHR での非同期リクエストでも出来るじゃん」と思ってしまいますね。

BigPipe のキモは XHR で非同期にリクエストすることと違い、最初の HTML を要求するリクエストの際に付随するページフラグメント郡も同時にリクエストされることです。また、最初の HTML リクエストと同じコネクションを用いてこれらのレスポンスが非同期に返却されます。

つまり、全体として一本のコネクションをもって、全てのレスポンスが非同期に返されます。

この性質により、次の観点でスピードを向上させることが出来ます。

  • 全てのデータをデータベースから取得し終えてから一括でサーバサイドレンダリングすることと比べたとき
    • ページの表示のために、データベースのクエリの結果を全て待たなくてよいため、飛躍的な体験的速度の向上が見込まれます
  • XHR に比べたとき
    • 最初の HTML を返してから XHR を行なうまでの時間がカットされます。
    • XHR で生じるリクエストにおいてのレイテンシがカットされます。
    • 多数の XHR リクエスト実行にかかるコストがカットされます。

ちなみに、ユーザの体感速度でみたとき、これくらい速くなるらしいです。(原文: 75th percentile user perceived latency for seeing the most important content in a page)

XHR だと、この指標はむしろ落ちてしまうのではないかと思います。

具体的な実装

前提知識

BigPipe は具体的には Chunked transfer encoding を用いて実装されています。 早い話が HTTP 上で「ストリーミング」が出来るプロトコルです。 これは特殊なプロトコルという訳ではなく、Rails でも普通にページを表示すると Chunked transfer encoding になったりしてました。

実装も簡単です。Header に Transfer-Encoding: chunked を指定して、どんどんレスポンスに内容を書いていくだけ。 ブラウザは内容が書かれたタイミングで、書かれた分の内容をすぐに取得することが出来ます。

BigPipe の実装

ここまで理解すれば BigPipe は簡単です。おおむね、次のような動作をします。

  1. ブラウザはサーバにリクエストを行ないます。
  2. サーバは Chunked transfer encoding を用いて、まずページの枠組みをレスポンスに書き込みます。
    • この段階でブラウザはページを表示出来ます。
    • 同時に、必要なページフラグメントを返すための処理をし始めます。(主にデータベースへのクエリ発行)
  3. サーバはページフラグメントの準備が完了するたびに、順不同でレスポンスに内容を書き込みます。
    • ブラウザはページフラグメントを受け取り、ページに埋め込んでいきます。(JS でハンドリングされます。)
  4. 全てのページフラグメントが返却し終えたら、レスポンスの返却を終了とします。

より詳しくは原文を参照ください。 BigPipe: Pipelining web pages for high performance

参考文献