以下斜め読んだ内容

pseudo translation of useful posts, book reviews, remarks,etc. twitter: feeddict

C.Scott Andreas「C500k in Action at Urban Airship」

Urban Airship Blog 2010.8.24のエントリ

C500k in Action at Urban Airship

  • push通知サービスの代行サービスをやってるUrban Airshipのシリーズ物のエントリ1回目
    • (補足)OpenID関連のサービスやってたVidoopという死亡したスタートアップ(パテントは会社ごと買収されてた)の元メンバーがやってる
  • Amazon EC2でクラウド破産は避けたい
  • python/c/c++/node.js/java/scalaと色々作っては試した
  • NIO(java)に落ち着いた。現在1インスタンスでc500k(使用メモリ2.5GB)
以下斜め読んだ内容
  • AirMail PushというサービスのAndroid版の開発を前からずっとやってる
  • AirMailのサービスで必要とされること
    • 数百万単位のデバイスへの同時接続をサポートできるサーバが必要
    • サーバとデバイス間で永続的なソケット通信
    • リアルタイムでデバイスにメッセージをpushできる
    • 使われてないソケットを検出して閉じる
    • ネットワーク外に出たユーザ・ネットワーク内に復帰したユーザとのコネクションのコントロール
  • push通知サービスの裏側で稼動してるサーバの設計では色々問題にぶつかって解決してきた
  • サーバーを何度も設計しなおした
  • 色々やった結論。JavaのNIOを使ったアーキテクチャ
    • スレッド/イベント/キューベースのハイブリッド型
  • おさらい:デバイスへのpush通知に必要なこと
    • 永続的なソケット通信
  • Urban Airshipのpush通知用プラットフォームでは数百万単位での同時接続が必要
    • 各ノードが大量の同時接続を維持しながらメッセージの受信やpush通知をエレガントにさばくことが必要になる
  • 1ノードでどれだけ同時接続をさばけるかが重要
    • 1万/nodeの場合
      • 100万デバイスだったらサーバ100台必要で、1000万デバイスへのpush通知だったら・・・・
      • 金がかかりすぎる・金が足りないということになる
  • Helium
    • 初代エッジサーバ
    • pythonのEventlet使った
  • Eventletはいいライブラリ
    • 軽量のコルーチンで並列処理
  • Eventletのソケットの実装は優秀
    • コンテキストスイッチが手軽でオーバーヘッドも少ない
      • この点スレッドベースのサーバーと対照的
    • 大量のコネクションをコントロールしやすい
  • Eventletベースで作ったHeliumはコードもクリーンで理解しやすかった
    • pythonもよかったし、Eventletのようなライブラリを生み出すpythonコミュニティも気に入ってた。
  • Eventletベースの初代Heliumのパフォーマンス
    • 3.7万接続
      • アプリケーションレベルでのkeep-aliveを維持できてるのが基準
      • EC2の1インスタンスで、使用メモリ1.7GBを上限に設定
    • パフォーマンスには一応満足。でもこのレベルだとまだまだインスタンスが沢山必要になる(=金がかかる)
  • インフラには投資はコンスタントにやっている
    • 安定さとパフォーマンスの向上のためには必要
  • われわれはインフラ屋というよりはプログラマの方が向いてる
    • よりパフォーマンス面でもコスト面でも効率のいいソフトウェアを書きたいと思ってる
    • なので作り直し。
  • Eventletベースの初代Heliumよりもパフォーマンスの優れたソケットサーバーの探求を始めた
    • 色々ためした
      • C/C++ベースの[e]poll
      • スレッド/イベントベースのjavaの実装やscalaの実装
      • 人気のNode.js
      • etc..
    • 完全にスレッドベースのjavaでの実装
      • 同時接続数が数千になったら使い物にならない。これは予想通り
    • Cベースでの実装
      • 低レベルすぎて実装大変。
      • プロジェクトの変化のスピードについていけない
  • javaのNIOライブラリ
    • NIO:new/non-blocking IO
    • 軽く試したらかなり優秀だったので、色々検証してみた
    • NIOだけの実装、Nettyの上にNIOのせた実装、Scala版Nettyの上にNIO載せた実装。この3つを比較

ベンチマーク1(最大同時接続数)

Implementation Connections Memory Used
Java Pure NIO 512,000 + 2.5 GB
Java w/Netty 330,000 2.2 GB
Scala w/Netty 173,000 1.5 GB

ベンチマーク2(コネクションごとのメモリ使用の効率性)

Implementation Connections Memory Used Delta
Java Pure NIO 80,000 + 581 MB 1x
Java w/Netty 80,000 711 MB 1.3x
Scala w/Netty 80,000 937 MB 2.26x
  • 同時接続数51.2万までアップ(Eventletベースの初代Heliumの約15倍)
    • パフォーマンス、コスト、管理コスト全てで改善された
  • ボーナスポイント
    • NIOベースでプラットフォームの実装(=必要なapplication logic/keepalive/message passingの実装)段階でもパフォーマンスは落ちなかった
  • NIO+Netty(java/scala)版
    • Netty/javaだとCPU使用率が100%まで跳ね上がるが、コンソールに何も出ないが、例外処理周りがボトルネックになってるかも