SlideShare a Scribd company logo
Inside Mobage Platform Toru Yamaguchi (=zigorou) DeNA Software Engineer [email_address] http://d.hatena.ne.jp/ZIGOROu/ http:// twitter.com/zigorou /
Y!mobage Released
ngmoco 
Agenda How to make OpenSocial Container 情報の少ない  OpenSocial Container  の作り方の概要について Shindig  の何を利用して何を使わないか Inside Social RESTful API & JSON-RPC API  大規模ウェブアプリケーションの実際 Inside Social Activity with Q4M  Q4M  を利用した数万 qps を捌く非同期ジョブ
1. How to make OpenSocial Container  Shindig を利用した OpenSocial Container の作り方
What is OpenSocial Container PC  の世界だと次のような定義 JavaScript API  を提供している Gadget XML  のパース&レンダリングが可能 また自ずと以下のような機能も要求される アプリケーションを実行する為の画面 Activity (Streams)  を流すためのフィード画面 Gadget  を管理する為のツール  (c.f. DeNA Developer Site) などなど…
Terms (1) Container SNS  本体の事だと思っていい Gadget Server Gadget XML  をパース&レンダリングするサーバー gadgets.io.makeRequest()  などを中継する JavaScript API  のコードをサーブする JavaScript API Appliacation  を作る際に使える  JavaScript  の  API Social Data (Person, Activity, Message, AppData)  などにアクセス出来る
Terms (2) Core & Social API JSON-RPC  と  RESTful API  の両方 JSON-RPC  は表向きに出している物ではなく  JavaScript API  の  Social Data  アクセスのバックエンドとして利用 RESTful API  はサーバー間でのソーシャルデータへのアクセスに利用 Mobile  向けはモバイル用の  Gadget Server ( コンテンツ  Proxy Server)  が発行する  Access Token  を原則要する  OAuth  でアクセス。 PC  向けはすべて  Consumer Request (2-legged OAuth)
Terms (3) View canvas, home, profile  などの  view  がある canvas  はいわゆる一番でかい画面 日本のガラケー向けに  mobile  という  view  もあります OpenSocial WAP Extension http:// wiki.opensocial.org/index.php?title = Opensocial-wap-extension   yoichiro++, weboo++
Canvas View yahoo-mbga.jp mbga-platform.jp
Apache Shindig (1) OpenSocial Container  の唯一のリファレンス実装 MySpace  を除くほぼ全ての  OpenSocial Container  で採用されていると思われる Java 版と PHP 版がある DeNA, mixi  では  Java  版を採用 GREE  は  PHP  版 Gadget Parsing & Rendering JavaScript API JSON-RPC & RESTful API
Apache Shindig (2) どこまで使えるか Gadget Parsing & Rendering はほぼそのまま使える 認証部は自前で結合しないといけない JavaScript API は自分たちの環境に合わせて結構手直ししないといけない またオレオレ API は当然自分で実装しないとだめ RESTful & JSON-RPC は実際のデータアクセス部分を Java or PHP で記述しないと使えない
Apache Shindig (3) Shindig  や  OpenSocial  と付き合う為のベストプラクティス Shindig  は唯一のリファレンス実装ですが Spec  違反はざらなので転んでも泣かない! 何を使い何を作るか Container (SNS)  は連携部も含め当然ながら自前で作る Gadget Parsing & Rendering  は仕様がごついのと原則ほとんどそのまま使えるので  Shindig  の物を使う JavaScript API  は手直し&追加で対応可能 Social API (RESTful & JSON-RPC)  は自前でデータバインディングを実装しないと使えない  結論 Social API  は自前で実装する
OpenSocial Architecture Container Gadget Server JavaScript API Social Data API (JSON-RPC/RESTful) Application Gadget XML Request with Authentication Fetch & Parse Render Execute XMLHttpRequest Gadget RPC External API XMLHttpRequest HTTP Request/Response Iframe Contents
Authentication (1) Security Token  方式と  OAuth  の二通り 通常は  Security Token  を  Iframe  に渡す  URL  中にクエリストリング  (st  値 )  として渡す つまり  Container (SNS)  側での  Authentication  情報を  Security Token  にして  iframe  に渡さないとだめ Gadget Servlet  側で  Authentication  Rendering  された  HTML  中に  JavaScript  で再利用可能な形で記述される 以降、 Social API  を間接的に利用する  opensocial.DataRequest  や  gadgets.io.makeRequest()  などを  JavaScript  から利用する場合にこの  Security Token  を用いる
Authentication (2) Container Gadget Server JavaScript API Social Data API (JSON-RPC/RESTful) Create and Pass  Security Token Application Embed Security Token As JavaScript Code Signed Request using Security Token Signed Request using Security Token Gadget RPC using Security Token
Authentication (3) #!/usr/bin/perl   use strict ;  use warnings ;  use  File::Slurp  qw(slurp) ;  use  Shindig::Authen::Java::BasicBlobCrypter;  use  Shindig::Authen::Java::BlobCrypterSecurityToken;  my  (  $container ,  $domain  ) = (  'default' ,  'mbga-platform.jp'   );  my   $master_key  = slurp( '/path/to/securityTokenKey' );  my   $crypter  = Shindig::Authen::Java::BasicBlobCrypter-> new (  master_key  =>  $master_key   );  my   $token  =  Shindig::Authen::Java::BlobCrypterSecurityToken-> new (  $crypter ,  $container ,  $domain  );  my   $authen_info  = +{  app_id  =>  12000120 ,  owner_id  =>  10020 ,  viewer_id  =>  10021 ,  };  for  ( %$authen_info ) {  $token -> $_ (  $authen_info ->{ $_ } );  }  warn   $token ->encrypt;
Authentication (4) という訳で作った Shindig::Authen::* Security Token  の  encrypt/decrypt  が出来る Shindig (Java 版 )  互換 まだオープンソースではない ニーズがあれば公開しようかなと ちなみに  key file  は以下のように作ります dd if=/dev/random bs=32 count=1  | openssl base64 > /tmp/key.txt via org.apache.shindig.common.crypto.BasicBlobCrypter
Shindig + Social API Gadget Server JavaScript API Social Data API (JSON-RPC/RESTful) Shindig Chariot (mobage Open Platoform API) Reverse Proxy (lighttpd) /gadgets/* /social/* XMLHttpRequest Same Origin Policy  のため 同一ドメインに  Shindig  と  API  を  Reverse Proxy  で実現する
Other things その他にやるべきこと config/container.js  security token  を有効にする lockedDomain (app  ごとに異なるドメイン ) XHR  の  Same Origin Policy  Domain Cookie  features  以下の  JS  の手直しや追加 JSON-RPC  とのやり取りの部分とか gadgets.rpc  の実装とか  (updateToken  など ) カスタムの  JS API  の実装とか
2. Inside Social API Plack  を使った  Social API  の作り方
Domain Specific Web API  を作るに当たって Spec  に極力準拠したい 手堅い実装を心がける、テスト書くのは当たり前 テスト自体は多分  xaicron  さんが前のトークで話してるはず! 軽く速くシンプルかつ可読性も担保したい 余計な機能や汎用性は要らない。 最低限の階層  (Dispatch -> Filter -> Controller -> API Service -> DataSource Service) 既存の  Web Application Framework  は使わない 好きなモジュールをチョイス Plack  あるし自分で目的に特化した奴を作ればいいよね
Basic Architecture Web Application Server  の構成 lighttpd  最初のチョイスから結局変えてない もし改めてやり直すなら多分  Apache  使いますw Plack with FastCGI External Server rpm  管理の都合上、 Starman/Starlet  はまだ使ってません もし改めてやり直すなら  rpm  で  CPAN  モジュールの管理はしないと思いますw それ  local::lib  で配布すれば出来るよ!
Dispatcher 文字通りの司令塔 Routing (PATH <-> Controller)  の決定 use Router::Simple tokuhirom++ Controller  の初期化、実行 config  で指定した  Filter  を実行出来る Intercepting Filter (via J2EE ServletFilter) Config メインはひとつ、環境別の差分は別に記述出来る 原則  Perl (.pl)  形式で記述 YAML  書けない
Intercepting Filter FilterChain Filter1 Filter2 Controller 順方向は  FilterChain  経由で 登録された  Filter  が順次実行される 逆方向は順方向と逆の順序で 処理が戻ってくる
Model (1) API Service (1) API  でやるべきロジックを記述する DataSource  へのアクセスロジックをフローに応じて呼び出して最終的に戻り値を  Formatter  に渡す 戻り値はたとえば  Array<Person>  みたいなデータ構造を表現している 但しこのデータ構造はオブジェクトではなく 原則  HashRef, ArrayRef, Scalar  のみで表現  ( 無駄なインスタンスは作らない )
Model (2) API Service (1) 特にリクエスト毎にインスタンス化するような設計ではないので  Object::Container  に突っ込んでおく fork  前にインスタンス化しておく  (CoW) 環境ごとに異なる設定値は  Config  で制御  (GlobalID  の  prefix  である  mbga.jp  みたいな文字列とか ) また好きな時に異なる  API Service  に差し替える事が可能 typester++
Model (3) DataSource Service (1) DB (MySQL)  や  Memcached  などへのアクセス DB  アクセスに関しては次のようなモジュールで全て記述している DBIx::DBHResolver DBIx::Connector SQL::Abstract::Limit + SQL::Abstract::Plugin::InsertMulti DBIx::AbstractSequence (not exists CPAN)
Model (4) DataSource Service (2) Memcached  は接続情報が  Memcached  にあると言うモバゲーの流儀に従っている 起動時に設定値を取得したら後は  Cache::File  に数十分間キャッシュしておく この辺りのコードは  Web Application  以外にもバッチや  Q4M  の  worker  などでも再利用 例によって  Object::Container  で省メモリかつ差し替え可能な実装
DBIx::DBHResolver (1) 大量にある  DB  へのハンドルを管理する Written by Kosuke Arisawa (DeNA) Maintainance by zigorou ^^ その他の特徴として オブジェクトとしてもクラスとしても動作する 各種設定ファイルで記述可能 原則は  DBI  での接続だが  DBI  のサブクラスや  DBIx::Connector  に書き換えたりも可能  Sharding  した  DB  の管理も楽 Hash, List, Range  などの分割手法に対応 Sharding  に用いられるキー郡を各ノードごとに分配する事が出来る
DBIx::DBHResolver (2) use  DBIx::DBHResolver;  my   $r  = DBIx::DBHResolver-> new ;  $r ->config(+{  connect_info  => +{  main_master  => +{  dsn  =>  'dbi:mysql:‘, user  =>  'master_user' ,  password  =>  '' ,  attrs  => +{  RaiseError  =>  1 ,  AutoCommit  =>  0 , },  },  main_slave  => +{  dsn  =>  'dbi:mysql:' ,  user  =>  'slave_user' ,  password  =>  '' ,  attrs  => +{  RaiseError  =>  1 ,  AutoCommit  =>  1 , },  },  },  });  my   $dbh_master  =  $r -> connect ( 'main_master' );  $dbh_master -> do (  'UPDATE people SET ...' ,  undef , ... );  my   $dbh_slave  =  $r -> connect ( 'main_slave' );  my   $people  =  $dbh_slave ->selectrow_hashref(  'SELECT * FROM people WHERE id = ?' ,  undef ,  20   );
DBIx::DBHResolver (3) connect_info 各サーバーの接続情報をノード名をキーとして記述する 接続先ホスト名は  DeNA  の場合、 MyDNS  を使っていて、 Slave  はさらに  DNSRR  としている clusters ノード郡と  sharding  アルゴリズムをクラスタ名をキーとして記述する
DBIx::DBHResolver (3) use  DBIx::DBHResolver;  my   $r  = DBIx::DBHResolver-> new ;  $r ->config(+{  connect_info  => +{  PEOPLE001_W  => +{ ... },  PEOPLE002_W  => +{ ... },  },  clusters  => +{  PEOPLE_W  => +{  strategy  =>  'Key' ,  nodes  => [ qw/PEOPLE001_W PEOPLE002_W/ ] }, },  });  ### connect to PEOPLE001_W   my   $dbh  =  $r -> connect (  'PEOPLE_W' ,  10  );
DBIx::DBHResolver (4) DBIx::DBHReslver  での  Sharding  の実施 あらかじめ何でどう分割するか決めておく user  の  id  だったり  application  の  id  だったり また  JOIN  の利用も適材適所で 一律  JOIN  しないという戦略は誤り 分割しててもしてなくても決めた  key  の値を渡すことによって特にエラーにならず、設定どおりの  $dbh  が返ってくるので、ちょっと直せば原則動く。 但し  sharding  の  key  を複数利用した処理などはこれだけではうまく記述できない
DBIx::DBHResolver (5) use  DBIx::DBHResolver;  my   $resolver  = DBIx::DBHResolver-> new ;  $resolver ->config(+{  clusters  => +{  MASTER  => +{  nodes  => [ qw/MASTER001 MASTER002 MASTER003/ ],  strategy  =>  'Key' , } },  connect_info  => +{  MASTER001  => +{ ... },  MASTER002  => +{ ... },  MASTER003  => +{ ... }, }, }); my   @keys  = (  3  ..  8  );  my   %node_keys  =  $resolver ->resolve_node_keys(  'MASTER' ,  \@keys  );  ### %node_keys = ( MASTER001 => [ 3, 6 ],  ### MASTER002 => [ 4, 7 ], MASTER003 => [ 5, 7 ] )   while  (  my  (  $node ,  $keys  ) =  each   %node_keys  ) {  #  それぞれのノードで処理する  (bulk insert  とか ) process_node(  $node ,  $keys  );  }
DBIx::DBHResolver (6) resolve_node_keys()  による  key  の分類 Activity Streams  の処理などで使ってます 自分の友達全てのタイムラインに  Activity  フィードを飛ばす際とか またこの処理自体も実際に  sharding  されてない環境でも同様に動きます^^ という訳で ある程度大きな環境になったら是非利用してみて下さい
DBIx::Connector (1) DBI  の薄い  wrapper シンプルなインターフェース transaction ping reconnect fork/thread safe 基本はコードリファレンスにやりたい処理を書く その  CodeRef  の引数に接続先の  $dbh  が渡される $dbh_foo, $dbh_bar  とかいろいろ書かなくて良くなるのも利点
DBIx::Connector (2) use  DBIx::Connector;  use  Try::Tiny;  my   $conn  = DBIx::Connector-> new (  $dsn ,  $user ,  $pass ,  $attrs  );  try {  $conn ->txn(  sub   {  my   $dbh  =  shift ;  ### transaction   $dbh ->commit;  ### Don’t forget commit!!! } );  }  catch {  my   $e  =  $_ ;  ### error handling   };
SQL::Abstract::* (1) SQL  は原則  SQL::Abstract  で記述する 但し複雑な  JOIN  や  GROUP BY  だとかはそのままヒアドキュメントで記述する SQL_CALC_FOUND_ROWS  だとか  FORCE INDEX, INSERT IGNORE  とかは無理やりどうにかする そろそろ新しい  SQL  生成モジュールがほしい所 例によってアプリケーション全体でひとつのインスタンスでかまわないので  Object::Container  に入ってます
SQL::Abstract::* (2) LIMIT, OFFSET  を使いたい SQL::Abstract::Limit  で出来るよ! Bulk insert  したい SQL::Abstract::Plugin::InsertMulti  で出来るよ! ON DUPLICATE KEY UPDATE  も出来るので  (duplicate  が )  多い日でも安心です! bulk insert  は  1000  件程度を上限にしてやるのがいいです 速度や  Too many connection, Max allow packets  の問題
MySQL (1) MySQL Partitioning  の利用  (1) 主に日時による  Range Partitioning  を利用しています モバゲーマイページの「友達のゲーム状況」、「ゲームからのお知らせ」の辺り こちらはパージの手軽さが一番の利用目的 一部  List Partitioning  も使っています TextData API  の  status  値で  List Partitioning  してます UNIQUE  制約が犠牲になったり外部キーが貼れなくなったりします 常にそれらとのトレードオフである事を考える
MySQL (2) MySQL Partitioning  の利用  (2) EXPLAIN PARTITIONS SELECT … その  SQL  でどのパーティションが選択されるかまで分かる パーティションが絞り込めれば速度が上がる インデックスとの併用なども考えたりしてます ALTER TABLE tablename ADD PARTITIONS 追加もすぐ終わります ALTER TABLE tablename DROP PARTITIONS 削除もすぐ終わります
MySQL (3) TRIGGER  の積極的な利用 TRIGGER  のメリットは大体デメリットだったりしますけど アプリケーション変更しなくても特定の処理が出来るけど、アプリケーション外で起こっているのは気持ち悪いとか ストアドプロシージャ構文が馴染めないとかw どのあたりで使ってるか サマリテーブルの更新 テーブルスキーマの追加や変更時 Partitioning  されたテーブルの  unique  制約 そもそも  Partitioning  するなって話でもあるw 特定の条件時に  Q4M  への  insert  とか
MySQL (4) TRIGGER  とスキーマ変更 例えば  Range Partitioning  されていない複数のテーブルがある 親にしか日時カラムが無い 子テーブルも同様に  Range Partitioning  したい 新しい系統を用意して、新スキーマの日時カラムは親の日時をトリガーで入れる トリガーは通常  mysqldump  だとデータのインポート後に適用されるが、日時カラムに  default  値を設定した上で事前にトリガーを定義してデータのインポートをする
MySQL (5) TRIGGER + Q4M でテーブルを自在に変更 これはまだ検証中です! 特定の slave に Q4M をインストールしておく 特定の slave だけにトリガーを作り、特定の条件の際に Q4M に queue を insert する 既存のテーブルの特定の部分だけを抽出してメッセージに出来る 垂直分割でまったく新しいテーブルにデータをコピー出来る レプリケーション開始前の元データの生成はさっきのスキーマ変更時のトリガー利用と同じ
MySQL (6) Old People Old Friend People Queue Friend Queue New People New Friend People Replication Worker (Perl) Friend Replication Worker (Perl) Insert queue by trigger queue_wait() insert/update/delete insert/update/delete Slave Slave Master replication replication
3. Inside Social Activity with Q4M Q4M を利用した 大規模メッセージングシステム
Q4M usage 使い方はとてもシンプル SELECT * FROM table_name WHERE queue_wait( ‘table_name’, wait_time ); enqueue  されるまで  wait_time(sec)  待って、 enqueue  されたら 1 レコードだけ取得できます SELECT queue_end() dequeue SELECT queue_abort() queue  を元に戻す
Worker (1) Q4M  や  Gearman  を使った  worker  の実装 Parallel::Prefork  を使えば簡単に書けます この辺りは  kazuho  さんのトークや  tokuhirom  の部ログにいろいろ書いてあります 後は  daemontools  で起動する API  で説明した  DataSource Service  を再利用する形で簡単に実装出来ます
Worker (2) Worker  の設定と配置 原則どのサーバーにも同じソースコードと設定ファイルが  deploy  されてます 但し、サーバーごとに異なる設定をしたい場合があります  ( あとで説明 ) ~/etc/sysconfig/production/daemon_env  のような設定ファイルを共通で  deploy 上記の設定を  /etc/sysconfig  以下に特定のルールでコピー 以降、上記のファイルは必要に応じて設定値を書き換える場合があります
Scaling Q4M (1) enqueue  が凄まじい環境での  Q4M enqueue  される順序が重要でない場合は、同じ用途の  Q4M  を複数台用意して、 Round Robin  で  insert  すれば解決 とも行かなかった。。。 理由は実際に  worker  が行う処理時間に  queue  の中身によって大きくばらつきがある場合に、 queue  の残数が均衡しないと言う問題が出た
Scaling Q4M (2) Q4M Worker (Perl) insert queue (DNSRR) Q4M queue_wait() and Running jobs (DNS RR)
Scaling Q4M (3) Q4M Worker (Perl) insert queue (DNSRR) Q4M queue_wait() and Running jobs Worker (Perl)
Scaling Q4M (4) queue_wait()  する  worker  からの接続先も  DNSRR  すると、ゆらぎが収まらない場合がある worker  は特定のサーバーだけを見るようにする またこうしておけば万が一障害が起きた場合にも切り替えが楽 手動  compaction  も片方サービスアウトしてゆるりと対応ってのも可能になる 先の設定値をサーバーごとに異なる設定云々 worker  の動作するサーバーごとに異なる  Q4M  に接続したいというニーズからでした 他にも急な負荷の増大のときに一時的にプロセス増やすとかそういうのでいちいち  deploy  なんてやってられないw
Other topics 最近の事例 最新データを元に  memcached  にキャッシュを作りに行く処理が重たい やたらと  CPU  を食う orz… 色々試して見ると  Cache::Memcached::Fast  に  no_wait  をつけると解決するらしい  (by xaicron) no_wait  は確認なしに  set()  を完了しちゃう設定
4. Conclusion まとめ
Conclusion Shindig  を使った  OpenSocial  コンテナ そこまでびっくりするほど大変じゃないです 但し実用レベルまでだと色々と(ry API  のような画面を伴わない  Web App そのドメイン特化のアプリケーションとしてフルスクラッチで書くのもいいよ Plack  や他の  CPAN  モジュールがあるので後は設計の妙 Q4M  の利用 Mobage Platform  の根幹部分を日々担ってるので相当な負荷に耐えられます でも初めて体験するような事も^^
Thanks & Questions? ご清聴ありがとうございました 時間余ってたら質問?

More Related Content

PDF
Mobage Connect と Identity 関連技術への取り組み - OpenID Summit Tokyo 2015
PDF
OAuth 2.0 Web Messaging Response Mode - OpenID Summit Tokyo 2015
PDF
【とらラボLT】go言語でのweb apiの作り方3選
PDF
IETF94 M2M Authentication関連報告
PPTX
革新的ブラウザゲームを支えるプラットフォーム技術
PDF
Use JWT access-token on Grails REST API
PDF
[SC07] Azure AD と Ruby で学ぶ OpenID Connect!
PDF
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
Mobage Connect と Identity 関連技術への取り組み - OpenID Summit Tokyo 2015
OAuth 2.0 Web Messaging Response Mode - OpenID Summit Tokyo 2015
【とらラボLT】go言語でのweb apiの作り方3選
IETF94 M2M Authentication関連報告
革新的ブラウザゲームを支えるプラットフォーム技術
Use JWT access-token on Grails REST API
[SC07] Azure AD と Ruby で学ぶ OpenID Connect!
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014

What's hot (19)

PDF
PlayFramework1.2.4におけるWebSocket
PDF
Authlete overview
PDF
Phpstudy44 Zend Frameworkが抱えている問題は多い。
PDF
Symfony2でより良いソフトウェアを作るために
PDF
これからのネイティブアプリにおけるOpenID Connectの活用
PPTX
DeNAtechcon_DeNAのセキュリティの取り組みと、スマートフォンセキュリティ(same-origin policy)
PPTX
ID連携のあるとき~、ないとき~ #エンプラ編
PDF
DeNAのゲーム開発を支える技術 (クライアントサイド編)
PPTX
Webシステム脆弱性LT資料
PDF
DeNAインフラの今とこれから - 今編 -
PDF
Effective web performance tuning for smartphone
PDF
GitHub APIとfreshで遊ぼう
PPTX
『OpenID ConnectとSCIMのエンタープライズ実装ガイドライン』解説
PDF
2014年を振り返る 今年の技術トレンドとDockerについて
PDF
ガールアックス:リアルタイム通信処理の効率的な実装
PDF
チラシルiOSでの広告枠開発
PDF
FFRKを支えるWebアプリケーションフレームワークの技術
PPTX
Mithril - 軽量/高速なMVCフレームワーク
PDF
[デブサミ2012]趣味と実益の脆弱性発見
PlayFramework1.2.4におけるWebSocket
Authlete overview
Phpstudy44 Zend Frameworkが抱えている問題は多い。
Symfony2でより良いソフトウェアを作るために
これからのネイティブアプリにおけるOpenID Connectの活用
DeNAtechcon_DeNAのセキュリティの取り組みと、スマートフォンセキュリティ(same-origin policy)
ID連携のあるとき~、ないとき~ #エンプラ編
DeNAのゲーム開発を支える技術 (クライアントサイド編)
Webシステム脆弱性LT資料
DeNAインフラの今とこれから - 今編 -
Effective web performance tuning for smartphone
GitHub APIとfreshで遊ぼう
『OpenID ConnectとSCIMのエンタープライズ実装ガイドライン』解説
2014年を振り返る 今年の技術トレンドとDockerについて
ガールアックス:リアルタイム通信処理の効率的な実装
チラシルiOSでの広告枠開発
FFRKを支えるWebアプリケーションフレームワークの技術
Mithril - 軽量/高速なMVCフレームワーク
[デブサミ2012]趣味と実益の脆弱性発見
Ad

Viewers also liked (20)

PDF
Japanese Companies in India 2014
PDF
Alnap lessons-urban-2012
PDF
Paychex Services
PDF
Guidetolisting
PDF
Manual mi primera página en html pre
PDF
Dng de-10012014
PDF
Seijo CGS Working Paper No. 10
PPT
Hawaiian Community Resources
PPTX
Marketing and segmentation ignite
PDF
security council report - IRAQ
PDF
News Update Nov. 2013
PDF
Dng de-02012012
PDF
Community Service Directory 2011
PDF
July 09 Almanac
PPTX
About AATJ for Affiliate Officers
PDF
Euromoney Indices Brochure
PDF
2015 Journal
DOCX
Chloe Resolution
PDF
2013-14 NLA Annual Report_R6
Japanese Companies in India 2014
Alnap lessons-urban-2012
Paychex Services
Guidetolisting
Manual mi primera página en html pre
Dng de-10012014
Seijo CGS Working Paper No. 10
Hawaiian Community Resources
Marketing and segmentation ignite
security council report - IRAQ
News Update Nov. 2013
Dng de-02012012
Community Service Directory 2011
July 09 Almanac
About AATJ for Affiliate Officers
Euromoney Indices Brochure
2015 Journal
Chloe Resolution
2013-14 NLA Annual Report_R6
Ad

Similar to Inside mobage platform (20)

PDF
AWS小ネタ集
PDF
20120423 hbase勉強会
PPTX
技術選択とアーキテクトの役割
PDF
大規模化するピグライフを支えるインフラ ~MongoDBとChefについて~ (前編)
PPTX
HTML5&API総まくり
PDF
ソーシャルゲームにおけるMongoDB適用事例 - Animal Land
PPT
OSC2008 Tokyo/Spring REST勉強夜会
PDF
Skinny Framework 進捗どうですか? #fud_scala
PPTX
Fluxflex meetup 2011 in Tokyo
PPTX
JavaFX + NetBeans環境におけるJenkinsの活用(Jenkins第六回勉強会)
PDF
ochacafe#6 人にもマシンにもやさしいAPIのエコシステム
PPTX
fluxflex meetup in Tokyo
PDF
Hypermedia: The Missing Element to Building Adaptable Web APIs in Rails (増補日本語版)
PDF
170827 jtf garafana
PDF
CouchDB JP & BigCouch
PDF
[Japanese] Skinny Framework で始める Scala #jjug_ccc #ccc_r24
KEY
activerecord-turntable
PDF
MongoDB2.2の新機能
KEY
沖縄Web+db勉強会 20121026
PDF
Drupal 8 - モダンなアーキテクチャのPHPベースOSS CMS
AWS小ネタ集
20120423 hbase勉強会
技術選択とアーキテクトの役割
大規模化するピグライフを支えるインフラ ~MongoDBとChefについて~ (前編)
HTML5&API総まくり
ソーシャルゲームにおけるMongoDB適用事例 - Animal Land
OSC2008 Tokyo/Spring REST勉強夜会
Skinny Framework 進捗どうですか? #fud_scala
Fluxflex meetup 2011 in Tokyo
JavaFX + NetBeans環境におけるJenkinsの活用(Jenkins第六回勉強会)
ochacafe#6 人にもマシンにもやさしいAPIのエコシステム
fluxflex meetup in Tokyo
Hypermedia: The Missing Element to Building Adaptable Web APIs in Rails (増補日本語版)
170827 jtf garafana
CouchDB JP & BigCouch
[Japanese] Skinny Framework で始める Scala #jjug_ccc #ccc_r24
activerecord-turntable
MongoDB2.2の新機能
沖縄Web+db勉強会 20121026
Drupal 8 - モダンなアーキテクチャのPHPベースOSS CMS

More from Toru Yamaguchi (18)

PDF
これからの Microservices
PPTX
技術選択とアーキテクトの役割 (要約版)
PDF
How to bake delicious cookie (RESTful Meetup #03)
KEY
JSON Based Web Services
PDF
Yapc asia 2011_zigorou
PPTX
ngCore engine for mobage platform
PDF
mbga Open Platform and Perl
PDF
Inside mbga Open Platform API architecture
PDF
Introduction OpenID Authentication 2.0 Revival
PDF
OpenID Mobile Profile
PDF
Introduction OpenID Authentication 2.0
PDF
OpenID 2009
PPT
Mobile Openid
PPT
Client Side Cache
PDF
The Security of OpenID Authentication 2.0
PPT
Customization of DBIC::Schema::Loader
PPT
Yadis/XRI and OpenID
PPT
OpenID 2.0 Quick Note
これからの Microservices
技術選択とアーキテクトの役割 (要約版)
How to bake delicious cookie (RESTful Meetup #03)
JSON Based Web Services
Yapc asia 2011_zigorou
ngCore engine for mobage platform
mbga Open Platform and Perl
Inside mbga Open Platform API architecture
Introduction OpenID Authentication 2.0 Revival
OpenID Mobile Profile
Introduction OpenID Authentication 2.0
OpenID 2009
Mobile Openid
Client Side Cache
The Security of OpenID Authentication 2.0
Customization of DBIC::Schema::Loader
Yadis/XRI and OpenID
OpenID 2.0 Quick Note

Inside mobage platform

  • 1. Inside Mobage Platform Toru Yamaguchi (=zigorou) DeNA Software Engineer [email_address] http://d.hatena.ne.jp/ZIGOROu/ http:// twitter.com/zigorou /
  • 4. Agenda How to make OpenSocial Container 情報の少ない OpenSocial Container の作り方の概要について Shindig の何を利用して何を使わないか Inside Social RESTful API & JSON-RPC API 大規模ウェブアプリケーションの実際 Inside Social Activity with Q4M Q4M を利用した数万 qps を捌く非同期ジョブ
  • 5. 1. How to make OpenSocial Container Shindig を利用した OpenSocial Container の作り方
  • 6. What is OpenSocial Container PC の世界だと次のような定義 JavaScript API を提供している Gadget XML のパース&レンダリングが可能 また自ずと以下のような機能も要求される アプリケーションを実行する為の画面 Activity (Streams) を流すためのフィード画面 Gadget を管理する為のツール (c.f. DeNA Developer Site) などなど…
  • 7. Terms (1) Container SNS 本体の事だと思っていい Gadget Server Gadget XML をパース&レンダリングするサーバー gadgets.io.makeRequest() などを中継する JavaScript API のコードをサーブする JavaScript API Appliacation を作る際に使える JavaScript の API Social Data (Person, Activity, Message, AppData) などにアクセス出来る
  • 8. Terms (2) Core & Social API JSON-RPC と RESTful API の両方 JSON-RPC は表向きに出している物ではなく JavaScript API の Social Data アクセスのバックエンドとして利用 RESTful API はサーバー間でのソーシャルデータへのアクセスに利用 Mobile 向けはモバイル用の Gadget Server ( コンテンツ Proxy Server) が発行する Access Token を原則要する OAuth でアクセス。 PC 向けはすべて Consumer Request (2-legged OAuth)
  • 9. Terms (3) View canvas, home, profile などの view がある canvas はいわゆる一番でかい画面 日本のガラケー向けに mobile という view もあります OpenSocial WAP Extension http:// wiki.opensocial.org/index.php?title = Opensocial-wap-extension yoichiro++, weboo++
  • 10. Canvas View yahoo-mbga.jp mbga-platform.jp
  • 11. Apache Shindig (1) OpenSocial Container の唯一のリファレンス実装 MySpace を除くほぼ全ての OpenSocial Container で採用されていると思われる Java 版と PHP 版がある DeNA, mixi では Java 版を採用 GREE は PHP 版 Gadget Parsing & Rendering JavaScript API JSON-RPC & RESTful API
  • 12. Apache Shindig (2) どこまで使えるか Gadget Parsing & Rendering はほぼそのまま使える 認証部は自前で結合しないといけない JavaScript API は自分たちの環境に合わせて結構手直ししないといけない またオレオレ API は当然自分で実装しないとだめ RESTful & JSON-RPC は実際のデータアクセス部分を Java or PHP で記述しないと使えない
  • 13. Apache Shindig (3) Shindig や OpenSocial と付き合う為のベストプラクティス Shindig は唯一のリファレンス実装ですが Spec 違反はざらなので転んでも泣かない! 何を使い何を作るか Container (SNS) は連携部も含め当然ながら自前で作る Gadget Parsing & Rendering は仕様がごついのと原則ほとんどそのまま使えるので Shindig の物を使う JavaScript API は手直し&追加で対応可能 Social API (RESTful & JSON-RPC) は自前でデータバインディングを実装しないと使えない 結論 Social API は自前で実装する
  • 14. OpenSocial Architecture Container Gadget Server JavaScript API Social Data API (JSON-RPC/RESTful) Application Gadget XML Request with Authentication Fetch & Parse Render Execute XMLHttpRequest Gadget RPC External API XMLHttpRequest HTTP Request/Response Iframe Contents
  • 15. Authentication (1) Security Token 方式と OAuth の二通り 通常は Security Token を Iframe に渡す URL 中にクエリストリング (st 値 ) として渡す つまり Container (SNS) 側での Authentication 情報を Security Token にして iframe に渡さないとだめ Gadget Servlet 側で Authentication Rendering された HTML 中に JavaScript で再利用可能な形で記述される 以降、 Social API を間接的に利用する opensocial.DataRequest や gadgets.io.makeRequest() などを JavaScript から利用する場合にこの Security Token を用いる
  • 16. Authentication (2) Container Gadget Server JavaScript API Social Data API (JSON-RPC/RESTful) Create and Pass Security Token Application Embed Security Token As JavaScript Code Signed Request using Security Token Signed Request using Security Token Gadget RPC using Security Token
  • 17. Authentication (3) #!/usr/bin/perl use strict ; use warnings ; use File::Slurp qw(slurp) ; use Shindig::Authen::Java::BasicBlobCrypter; use Shindig::Authen::Java::BlobCrypterSecurityToken; my ( $container , $domain ) = ( 'default' , 'mbga-platform.jp' ); my $master_key = slurp( '/path/to/securityTokenKey' ); my $crypter = Shindig::Authen::Java::BasicBlobCrypter-> new ( master_key => $master_key ); my $token = Shindig::Authen::Java::BlobCrypterSecurityToken-> new ( $crypter , $container , $domain ); my $authen_info = +{ app_id => 12000120 , owner_id => 10020 , viewer_id => 10021 , }; for ( %$authen_info ) { $token -> $_ ( $authen_info ->{ $_ } ); } warn $token ->encrypt;
  • 18. Authentication (4) という訳で作った Shindig::Authen::* Security Token の encrypt/decrypt が出来る Shindig (Java 版 ) 互換 まだオープンソースではない ニーズがあれば公開しようかなと ちなみに key file は以下のように作ります dd if=/dev/random bs=32 count=1 | openssl base64 > /tmp/key.txt via org.apache.shindig.common.crypto.BasicBlobCrypter
  • 19. Shindig + Social API Gadget Server JavaScript API Social Data API (JSON-RPC/RESTful) Shindig Chariot (mobage Open Platoform API) Reverse Proxy (lighttpd) /gadgets/* /social/* XMLHttpRequest Same Origin Policy のため 同一ドメインに Shindig と API を Reverse Proxy で実現する
  • 20. Other things その他にやるべきこと config/container.js security token を有効にする lockedDomain (app ごとに異なるドメイン ) XHR の Same Origin Policy Domain Cookie features 以下の JS の手直しや追加 JSON-RPC とのやり取りの部分とか gadgets.rpc の実装とか (updateToken など ) カスタムの JS API の実装とか
  • 21. 2. Inside Social API Plack を使った Social API の作り方
  • 22. Domain Specific Web API を作るに当たって Spec に極力準拠したい 手堅い実装を心がける、テスト書くのは当たり前 テスト自体は多分 xaicron さんが前のトークで話してるはず! 軽く速くシンプルかつ可読性も担保したい 余計な機能や汎用性は要らない。 最低限の階層 (Dispatch -> Filter -> Controller -> API Service -> DataSource Service) 既存の Web Application Framework は使わない 好きなモジュールをチョイス Plack あるし自分で目的に特化した奴を作ればいいよね
  • 23. Basic Architecture Web Application Server の構成 lighttpd 最初のチョイスから結局変えてない もし改めてやり直すなら多分 Apache 使いますw Plack with FastCGI External Server rpm 管理の都合上、 Starman/Starlet はまだ使ってません もし改めてやり直すなら rpm で CPAN モジュールの管理はしないと思いますw それ local::lib で配布すれば出来るよ!
  • 24. Dispatcher 文字通りの司令塔 Routing (PATH <-> Controller) の決定 use Router::Simple tokuhirom++ Controller の初期化、実行 config で指定した Filter を実行出来る Intercepting Filter (via J2EE ServletFilter) Config メインはひとつ、環境別の差分は別に記述出来る 原則 Perl (.pl) 形式で記述 YAML 書けない
  • 25. Intercepting Filter FilterChain Filter1 Filter2 Controller 順方向は FilterChain 経由で 登録された Filter が順次実行される 逆方向は順方向と逆の順序で 処理が戻ってくる
  • 26. Model (1) API Service (1) API でやるべきロジックを記述する DataSource へのアクセスロジックをフローに応じて呼び出して最終的に戻り値を Formatter に渡す 戻り値はたとえば Array<Person> みたいなデータ構造を表現している 但しこのデータ構造はオブジェクトではなく 原則 HashRef, ArrayRef, Scalar のみで表現 ( 無駄なインスタンスは作らない )
  • 27. Model (2) API Service (1) 特にリクエスト毎にインスタンス化するような設計ではないので Object::Container に突っ込んでおく fork 前にインスタンス化しておく (CoW) 環境ごとに異なる設定値は Config で制御 (GlobalID の prefix である mbga.jp みたいな文字列とか ) また好きな時に異なる API Service に差し替える事が可能 typester++
  • 28. Model (3) DataSource Service (1) DB (MySQL) や Memcached などへのアクセス DB アクセスに関しては次のようなモジュールで全て記述している DBIx::DBHResolver DBIx::Connector SQL::Abstract::Limit + SQL::Abstract::Plugin::InsertMulti DBIx::AbstractSequence (not exists CPAN)
  • 29. Model (4) DataSource Service (2) Memcached は接続情報が Memcached にあると言うモバゲーの流儀に従っている 起動時に設定値を取得したら後は Cache::File に数十分間キャッシュしておく この辺りのコードは Web Application 以外にもバッチや Q4M の worker などでも再利用 例によって Object::Container で省メモリかつ差し替え可能な実装
  • 30. DBIx::DBHResolver (1) 大量にある DB へのハンドルを管理する Written by Kosuke Arisawa (DeNA) Maintainance by zigorou ^^ その他の特徴として オブジェクトとしてもクラスとしても動作する 各種設定ファイルで記述可能 原則は DBI での接続だが DBI のサブクラスや DBIx::Connector に書き換えたりも可能 Sharding した DB の管理も楽 Hash, List, Range などの分割手法に対応 Sharding に用いられるキー郡を各ノードごとに分配する事が出来る
  • 31. DBIx::DBHResolver (2) use DBIx::DBHResolver; my $r = DBIx::DBHResolver-> new ; $r ->config(+{ connect_info => +{ main_master => +{ dsn => 'dbi:mysql:‘, user => 'master_user' , password => '' , attrs => +{ RaiseError => 1 , AutoCommit => 0 , }, }, main_slave => +{ dsn => 'dbi:mysql:' , user => 'slave_user' , password => '' , attrs => +{ RaiseError => 1 , AutoCommit => 1 , }, }, }, }); my $dbh_master = $r -> connect ( 'main_master' ); $dbh_master -> do ( 'UPDATE people SET ...' , undef , ... ); my $dbh_slave = $r -> connect ( 'main_slave' ); my $people = $dbh_slave ->selectrow_hashref( 'SELECT * FROM people WHERE id = ?' , undef , 20 );
  • 32. DBIx::DBHResolver (3) connect_info 各サーバーの接続情報をノード名をキーとして記述する 接続先ホスト名は DeNA の場合、 MyDNS を使っていて、 Slave はさらに DNSRR としている clusters ノード郡と sharding アルゴリズムをクラスタ名をキーとして記述する
  • 33. DBIx::DBHResolver (3) use DBIx::DBHResolver; my $r = DBIx::DBHResolver-> new ; $r ->config(+{ connect_info => +{ PEOPLE001_W => +{ ... }, PEOPLE002_W => +{ ... }, }, clusters => +{ PEOPLE_W => +{ strategy => 'Key' , nodes => [ qw/PEOPLE001_W PEOPLE002_W/ ] }, }, }); ### connect to PEOPLE001_W my $dbh = $r -> connect ( 'PEOPLE_W' , 10 );
  • 34. DBIx::DBHResolver (4) DBIx::DBHReslver での Sharding の実施 あらかじめ何でどう分割するか決めておく user の id だったり application の id だったり また JOIN の利用も適材適所で 一律 JOIN しないという戦略は誤り 分割しててもしてなくても決めた key の値を渡すことによって特にエラーにならず、設定どおりの $dbh が返ってくるので、ちょっと直せば原則動く。 但し sharding の key を複数利用した処理などはこれだけではうまく記述できない
  • 35. DBIx::DBHResolver (5) use DBIx::DBHResolver; my $resolver = DBIx::DBHResolver-> new ; $resolver ->config(+{ clusters => +{ MASTER => +{ nodes => [ qw/MASTER001 MASTER002 MASTER003/ ], strategy => 'Key' , } }, connect_info => +{ MASTER001 => +{ ... }, MASTER002 => +{ ... }, MASTER003 => +{ ... }, }, }); my @keys = ( 3 .. 8 ); my %node_keys = $resolver ->resolve_node_keys( 'MASTER' , \@keys ); ### %node_keys = ( MASTER001 => [ 3, 6 ], ### MASTER002 => [ 4, 7 ], MASTER003 => [ 5, 7 ] ) while ( my ( $node , $keys ) = each %node_keys ) { # それぞれのノードで処理する (bulk insert とか ) process_node( $node , $keys ); }
  • 36. DBIx::DBHResolver (6) resolve_node_keys() による key の分類 Activity Streams の処理などで使ってます 自分の友達全てのタイムラインに Activity フィードを飛ばす際とか またこの処理自体も実際に sharding されてない環境でも同様に動きます^^ という訳で ある程度大きな環境になったら是非利用してみて下さい
  • 37. DBIx::Connector (1) DBI の薄い wrapper シンプルなインターフェース transaction ping reconnect fork/thread safe 基本はコードリファレンスにやりたい処理を書く その CodeRef の引数に接続先の $dbh が渡される $dbh_foo, $dbh_bar とかいろいろ書かなくて良くなるのも利点
  • 38. DBIx::Connector (2) use DBIx::Connector; use Try::Tiny; my $conn = DBIx::Connector-> new ( $dsn , $user , $pass , $attrs ); try { $conn ->txn( sub { my $dbh = shift ; ### transaction $dbh ->commit; ### Don’t forget commit!!! } ); } catch { my $e = $_ ; ### error handling };
  • 39. SQL::Abstract::* (1) SQL は原則 SQL::Abstract で記述する 但し複雑な JOIN や GROUP BY だとかはそのままヒアドキュメントで記述する SQL_CALC_FOUND_ROWS だとか FORCE INDEX, INSERT IGNORE とかは無理やりどうにかする そろそろ新しい SQL 生成モジュールがほしい所 例によってアプリケーション全体でひとつのインスタンスでかまわないので Object::Container に入ってます
  • 40. SQL::Abstract::* (2) LIMIT, OFFSET を使いたい SQL::Abstract::Limit で出来るよ! Bulk insert したい SQL::Abstract::Plugin::InsertMulti で出来るよ! ON DUPLICATE KEY UPDATE も出来るので (duplicate が ) 多い日でも安心です! bulk insert は 1000 件程度を上限にしてやるのがいいです 速度や Too many connection, Max allow packets の問題
  • 41. MySQL (1) MySQL Partitioning の利用 (1) 主に日時による Range Partitioning を利用しています モバゲーマイページの「友達のゲーム状況」、「ゲームからのお知らせ」の辺り こちらはパージの手軽さが一番の利用目的 一部 List Partitioning も使っています TextData API の status 値で List Partitioning してます UNIQUE 制約が犠牲になったり外部キーが貼れなくなったりします 常にそれらとのトレードオフである事を考える
  • 42. MySQL (2) MySQL Partitioning の利用 (2) EXPLAIN PARTITIONS SELECT … その SQL でどのパーティションが選択されるかまで分かる パーティションが絞り込めれば速度が上がる インデックスとの併用なども考えたりしてます ALTER TABLE tablename ADD PARTITIONS 追加もすぐ終わります ALTER TABLE tablename DROP PARTITIONS 削除もすぐ終わります
  • 43. MySQL (3) TRIGGER の積極的な利用 TRIGGER のメリットは大体デメリットだったりしますけど アプリケーション変更しなくても特定の処理が出来るけど、アプリケーション外で起こっているのは気持ち悪いとか ストアドプロシージャ構文が馴染めないとかw どのあたりで使ってるか サマリテーブルの更新 テーブルスキーマの追加や変更時 Partitioning されたテーブルの unique 制約 そもそも Partitioning するなって話でもあるw 特定の条件時に Q4M への insert とか
  • 44. MySQL (4) TRIGGER とスキーマ変更 例えば Range Partitioning されていない複数のテーブルがある 親にしか日時カラムが無い 子テーブルも同様に Range Partitioning したい 新しい系統を用意して、新スキーマの日時カラムは親の日時をトリガーで入れる トリガーは通常 mysqldump だとデータのインポート後に適用されるが、日時カラムに default 値を設定した上で事前にトリガーを定義してデータのインポートをする
  • 45. MySQL (5) TRIGGER + Q4M でテーブルを自在に変更 これはまだ検証中です! 特定の slave に Q4M をインストールしておく 特定の slave だけにトリガーを作り、特定の条件の際に Q4M に queue を insert する 既存のテーブルの特定の部分だけを抽出してメッセージに出来る 垂直分割でまったく新しいテーブルにデータをコピー出来る レプリケーション開始前の元データの生成はさっきのスキーマ変更時のトリガー利用と同じ
  • 46. MySQL (6) Old People Old Friend People Queue Friend Queue New People New Friend People Replication Worker (Perl) Friend Replication Worker (Perl) Insert queue by trigger queue_wait() insert/update/delete insert/update/delete Slave Slave Master replication replication
  • 47. 3. Inside Social Activity with Q4M Q4M を利用した 大規模メッセージングシステム
  • 48. Q4M usage 使い方はとてもシンプル SELECT * FROM table_name WHERE queue_wait( ‘table_name’, wait_time ); enqueue されるまで wait_time(sec) 待って、 enqueue されたら 1 レコードだけ取得できます SELECT queue_end() dequeue SELECT queue_abort() queue を元に戻す
  • 49. Worker (1) Q4M や Gearman を使った worker の実装 Parallel::Prefork を使えば簡単に書けます この辺りは kazuho さんのトークや tokuhirom の部ログにいろいろ書いてあります 後は daemontools で起動する API で説明した DataSource Service を再利用する形で簡単に実装出来ます
  • 50. Worker (2) Worker の設定と配置 原則どのサーバーにも同じソースコードと設定ファイルが deploy されてます 但し、サーバーごとに異なる設定をしたい場合があります ( あとで説明 ) ~/etc/sysconfig/production/daemon_env のような設定ファイルを共通で deploy 上記の設定を /etc/sysconfig 以下に特定のルールでコピー 以降、上記のファイルは必要に応じて設定値を書き換える場合があります
  • 51. Scaling Q4M (1) enqueue が凄まじい環境での Q4M enqueue される順序が重要でない場合は、同じ用途の Q4M を複数台用意して、 Round Robin で insert すれば解決 とも行かなかった。。。 理由は実際に worker が行う処理時間に queue の中身によって大きくばらつきがある場合に、 queue の残数が均衡しないと言う問題が出た
  • 52. Scaling Q4M (2) Q4M Worker (Perl) insert queue (DNSRR) Q4M queue_wait() and Running jobs (DNS RR)
  • 53. Scaling Q4M (3) Q4M Worker (Perl) insert queue (DNSRR) Q4M queue_wait() and Running jobs Worker (Perl)
  • 54. Scaling Q4M (4) queue_wait() する worker からの接続先も DNSRR すると、ゆらぎが収まらない場合がある worker は特定のサーバーだけを見るようにする またこうしておけば万が一障害が起きた場合にも切り替えが楽 手動 compaction も片方サービスアウトしてゆるりと対応ってのも可能になる 先の設定値をサーバーごとに異なる設定云々 worker の動作するサーバーごとに異なる Q4M に接続したいというニーズからでした 他にも急な負荷の増大のときに一時的にプロセス増やすとかそういうのでいちいち deploy なんてやってられないw
  • 55. Other topics 最近の事例 最新データを元に memcached にキャッシュを作りに行く処理が重たい やたらと CPU を食う orz… 色々試して見ると Cache::Memcached::Fast に no_wait をつけると解決するらしい (by xaicron) no_wait は確認なしに set() を完了しちゃう設定
  • 57. Conclusion Shindig を使った OpenSocial コンテナ そこまでびっくりするほど大変じゃないです 但し実用レベルまでだと色々と(ry API のような画面を伴わない Web App そのドメイン特化のアプリケーションとしてフルスクラッチで書くのもいいよ Plack や他の CPAN モジュールがあるので後は設計の妙 Q4M の利用 Mobage Platform の根幹部分を日々担ってるので相当な負荷に耐えられます でも初めて体験するような事も^^
  • 58. Thanks & Questions? ご清聴ありがとうございました 時間余ってたら質問?