ハイパフォーマンスブラウザネットワーキング読了

ハイパフォーマンス ブラウザネットワーキング ―ネットワークアプリケーションのためのパフォーマンス最適化

ハイパフォーマンス ブラウザネットワーキング ―ネットワークアプリケーションのためのパフォーマンス最適化

個人的に、WebRTCは一冊本を読んだ方が良さそう。

TCP

チューニングノウハウ

  • 初期ウィンドウを増やす(initcwndは sysctl ではなく ip route で設定するものらしい)
  • スロースタートリスタートは無効に(idle状態になった後もウィンドウをリセットしない)
  • ウィンドウスケーリングは有効に(ウィンドウサイズを65535よりも大きくする)
  • TCP Fast Open(接続したことがあるピアに対して、事前にやり取りしたcookieを利用して、最初のsynパケットにデータを載せることで0RTTを実現する)*1

トンネル化

  • 新しいアプリケーションのためにウェルノウンポートを割り当てるのは現実的でない
  • ほとんどのRouter/Firewallが通す80番/443番を使う
  • しかし、80番はHTTP(ウェブ)に最適化されているため、HTTP以外のプロトコル(新しいアプリケーション)に問題を起こす可能性がある(キャッシュによるレイテンシ増加や、フィルタなど)
  • そのため、443番(HTTPS)によってトンネル化した経路を使う
  • WebSocket/SPDYがSSL/TLSを推奨するにはそういう理由もある

TLS

  • Server Helloと共にサーバー証明書(公開鍵が付く)をクライアントに配布し、それを受信したクライアントはRSA or DHにより共通鍵の共有プロセスを始める

RSA

DH

  • Diffie Hellmanは一時的なキーを使い、かつ、経路上にキーを流さない
  • 接続毎にキーが異なるため(前方秘匿性)、他のトラヒックを危険にさらすことはない
  • 自身の証明ができないため、中間者攻撃に対して脆弱

ALPN

  • Client Hello/Server Helloにより、プロトコルスイートを選択
  • これを利用するのがALPN(Application Layer Protocol Negotiation)
  • HTTPアップグレード(HTTP Headerの “Upgrade”)を使うと、余計に1往復かかるので、TLSの最初の1往復でSSL/TLS以外のプロトコル選択をやってしまう

TLS Session Resumption

  • ServerHelloにSession IDを含める
  • ネゴシエートした暗号スイート・共通鍵を、Session IDをキーとしてキャッシュしておく
  • ClientHelloにSession IDを含めることで、既存のSession情報を再利用可能

Session Ticket

  • サーバ側のキャッシュの負担を取り去るため、クライアント側のみにSession情報をキャッシュする
  • NewSessionTicketレコードをTSLハンドシェイクの最後に付与する
  • Session Ticketはサーバのみ所有する鍵で暗号化しておくことで、サーバのみが復号化できる

Mobile Network

  • 無線のエネルギー消費量とデータ転送量は比例しない
  • 電力消費を抑えるために、バースト的に送受信し、早くアイドル状態に戻す・戻るべき(ポーリングはアンチパターン
  • アイドル状態から接続状態への移行は、LTEで100msec、LTE-Advanceで50msec
  • ドーマント状態から接続状態への移行は、LTEで50msec、LTE-Advanceで10msec

インバウンドのデータフロー

  • P-GWはS-GWへ送信
  • S-GWはMMEに宛先端末の位置情報を問い合わせる
  • MMEは宛先端末の位置情報が無いとき、トラッキングエリアの全ての基地局にページングリストをブロードキャストする
  • 端末はアイドル状態のときも制御情報は受信しており、ページングリストを受信可能
  • ページングリストに自身の宛先が含まれていた場合、基地局に対して無線確立(無線リソース確保)リクエストを開始
  • 基地局はMMEに宛先端末の位置情報を回答
  • MMEは位置情報をS-GWに回答し、S-GWは基地局に送信

HTTP 1.x

  • HTTPパイプライン
  • 複数TCP接続の活用による並列化
  • ファイル結合によりRTTを減らす
  • 一つのドメインに対するTCP接続数は6に制限されているため、ドメインシャーディングにより異なるホスト名を一つのドメインに割り当てる(TCP接続数の制限は、IPアドレスではなく、ホスト名によって区別される)

PageSpeed

developers.google.com

Slides

Networking Best Practices (WWDC 2012, session 706)

HTTP 2.0

Based on SPDY

バイナリフレーミング

  • ストリーム > メッセージ > フレーム
  • フレームは新しい8バイトのヘッダを持つ
  • フレームのデータ部分に既存のHTTPヘッダとHTTPボディを載せる
  • HTTPヘッダのRequest行は:method, :scheme, :authority, :pathに分割された
  • それ以外のHTTPヘッダに変更無い(HTTP 1.xでHTTPヘッダに変更があってもそれはHTTP 2.0に吸収される)
  • メッセージには、DATA(HTTPボディ)、HEADERS(HTTPヘッダ)、PUSH_PROMISE、SETTINGS、などの種類がある(これはフレームヘッダのフレームタイプセクションで区別される)
  • ストリームにはストリームIDが付与されるため一意に識別できる

ヘッダ圧縮(HPACK)

  • zlibによるヘッダ圧縮は脆弱性(CRIME)が発見されたため使われない
  • よく使うヘッダはその値と共にHeader Tableで管理され、インデックスの指定で伝えることができる(Header Diff)
  • ヘッダをキャッシュすることで、全てのヘッダを毎フレーム送信する必要がなく、差分のみ送ればよい(Reference Set)

http://mew.org/~kazu/material/2014-hpack.pdf

サーバプッシュ

  • サーバはリクエストされたリソースから、すぐにリクエストされるであろうリソースを割り出し、オリジナルのリクエストに対するリスポンスを返すよりも前に、PUSH_PROMISEによってクライアントからのリクエストなしにそれらのリソースを返すことを知らせる(それらのリソースは別ストリームで返される)

XHR

  • ブラウザにおいて非同期通信を実現、Javascriptで制御
  • データ全体の受信を待たず順序処理するデータストリーミングには向いていないし、テキストのダウンロードしか対応していない、かつ、自分で(Javascriptで)ストリーミングを扱う処理を書く必要がある
  • データ全体を受信して接続を閉じるまで、逐次受信したデータを破棄することができない(メモリ効率悪い)
  • ロングポーリング(=Comet)で対応

CORS

  • あるOriginのスクリプトから、別のOriginへのリクエストを許可するための仕組み
  • サーバはAccess-Control-Allow-Originで、許可するOriginを返す
  • Credential request(Access-Control-Allow-Credentials : trueをレスポンスが含まなければブラウザは受信を拒否する)の場合は、Access-Control-Allow-Origin: *(ワイルドカード指定)は使えない

developer.mozilla.org

Server-Sent Events

  • シンプルで効率的なサーバ->クライアントのテキスト(UTF-8)通信方法として設計されたもの
  • HTTPヘッダはContent-Type:text/event-stream
  • ブラウザが落ちた場合は、Last-Event-IDで、最後に受け取ったストリーミングデータのIDをサーバに通知(IDはオプション)
  • バッファ効率良い

WebSocket

www.slideshare.net

qiita.com

  • 双方向・Anyフォーマットのサポート
  • ブラウザ以外でも使用可能(ws, wssスキーム)
  • sendは非同期であり、bufferedAmountを監視して、ブラウザ上に保持されているデータ量に応じて転送を行う
  • 1ビットでテキストかバイナリか、の区別をつける
  • メタデータはWebSocketとして定義されていないため、サブプロトコル(アプリケーションの一部)を使用する
  • WebSocketヘッダのマスク値を使ってペイロードをマスキングする(暗号化ではなく、WebSocketを理解しない中間者に対するキャッシュポイゾニングの防止)
  • メッセージはフレームに分割されるが、異なるメッセージのインターリーブができないのでHoLの影響を受ける
  • チャネルの導入による多重化拡張がある
  • HTTP Upgradeを利用して、WebSocketのバージョンやサブプロトコルネゴシエートも行う(Sec-WebSocket-xxxというHTTPヘッダ)
  • プロトコルをサポートしていることを知らせる手段として、Sec-WebSocket-Keyの値をハッシュ化&Base64エンコードしたものをSec-WebSocket-Acceptとして送信する
  • ブラウザからデータが見えないため、HTTPで有効なキャッシュなどのロジックは利用することができない
  • データがテキストであれば圧縮は有効だが、バイナリの場合はすでに圧縮されている可能性がある(jpgなど)ため、圧縮するかどうかの戦略はアプリケーションの仕事(圧縮に対応した拡張実装が準備されているようではある)

WebRTC

  • Web Real-Time Communication over SCTP/DTLS/UDP
  • WebRTCラッパ:SimpleWebRTC

ICE

  • NAT配下のピアのグローバルアクセス可能なアドレス(IPアドレス・ポート)を取得する必要がある
  • バックグラウンドのSTUNルックアップ(RTCPeerConnection(ice).onicecandidate)
  • STUNバインディングリクエストに対するレスポンスを受けることで完了、失敗したらTURNサーバへフォールバック
  • ICE候補はピア間のP2Pデータ用通信経路

シグナリングチャネル

  • P2Pデータ用通信経路を確立するための、制御用の通信経路。SIPなどが使われる。サーバ必要。

Trickle ICE

DTLS

  • データグラムのメッセージ指向TLS
  • ブロック暗号を用いることで、復号時のメッセージ間依存性を排除(パケロスに対する耐性)

SCTP

  • SCTP over UDPにより、UDPにはない輻輳制御を受け持つ
  • マルチパス転送が可能
  • IP直下で動作するトランスポートプロトコルだが、ネットワーク機器は対応していないのが実情

www.slideshare.net

*1:Linux Kernel 3.6/3.7でクライアント側/サーバ側でサポートされた