Inside Mobage Platform Toru Yamaguchi (=zigorou) DeNA Software Engineer [email_address] http://d.hatena.ne.jp/ZIGOROu/ htt...
Y!mobage Released
ngmoco 
Agenda <ul><li>How to make OpenSocial Container </li></ul><ul><ul><li>情報の少ない  OpenSocial Container  の作り方の概要について </li></ul>...
1. How to make OpenSocial Container  Shindig を利用した OpenSocial Container の作り方
What is OpenSocial Container <ul><li>PC  の世界だと次のような定義 </li></ul><ul><ul><li>JavaScript API  を提供している </li></ul></ul><ul><ul...
Terms (1) <ul><li>Container </li></ul><ul><ul><li>SNS  本体の事だと思っていい </li></ul></ul><ul><li>Gadget Server </li></ul><ul><ul>...
Terms (2) <ul><li>Core & Social API </li></ul><ul><ul><li>JSON-RPC  と  RESTful API  の両方 </li></ul></ul><ul><ul><li>JSON-RP...
Terms (3) <ul><li>View </li></ul><ul><ul><li>canvas, home, profile  などの  view  がある </li></ul></ul><ul><ul><li>canvas  はいわゆ...
Canvas View yahoo-mbga.jp mbga-platform.jp
Apache Shindig (1) <ul><li>OpenSocial Container  の唯一のリファレンス実装 </li></ul><ul><ul><li>MySpace  を除くほぼ全ての  OpenSocial Containe...
Apache Shindig (2) <ul><li>どこまで使えるか </li></ul><ul><ul><li>Gadget Parsing & Rendering はほぼそのまま使える </li></ul></ul><ul><ul><li...
Apache Shindig (3) <ul><li>Shindig  や  OpenSocial  と付き合う為のベストプラクティス </li></ul><ul><ul><li>Shindig  は唯一のリファレンス実装ですが </li></...
OpenSocial Architecture Container Gadget Server JavaScript API Social Data API (JSON-RPC/RESTful) Application Gadget XML R...
Authentication (1) <ul><li>Security Token  方式と  OAuth  の二通り </li></ul><ul><ul><li>通常は  Security Token  を  Iframe  に渡す  URL...
Authentication (2) Container Gadget Server JavaScript API Social Data API (JSON-RPC/RESTful) Create and Pass  Security Tok...
Authentication (3) <ul><li>#!/usr/bin/perl   </li></ul><ul><li>use strict ;  </li></ul><ul><li>use warnings ;  </li></ul><...
Authentication (4) <ul><li>という訳で作った </li></ul><ul><ul><li>Shindig::Authen::* </li></ul></ul><ul><ul><li>Security Token  の ...
Shindig + Social API Gadget Server JavaScript API Social Data API (JSON-RPC/RESTful) Shindig Chariot (mobage Open Platofor...
Other things <ul><li>その他にやるべきこと </li></ul><ul><ul><li>config/container.js  </li></ul></ul><ul><ul><ul><li>security token  ...
2. Inside Social API Plack  を使った  Social API  の作り方
Domain Specific <ul><li>Web API  を作るに当たって </li></ul><ul><ul><li>Spec  に極力準拠したい </li></ul></ul><ul><ul><ul><li>手堅い実装を心がける、テ...
Basic Architecture <ul><li>Web Application Server  の構成 </li></ul><ul><ul><li>lighttpd  </li></ul></ul><ul><ul><ul><li>最初のチ...
Dispatcher <ul><li>文字通りの司令塔 </li></ul><ul><ul><li>Routing (PATH <-> Controller)  の決定 </li></ul></ul><ul><ul><ul><li>use Ro...
Intercepting Filter FilterChain Filter1 Filter2 Controller 順方向は  FilterChain  経由で 登録された  Filter  が順次実行される 逆方向は順方向と逆の順序で 処理...
Model (1) <ul><li>API Service (1) </li></ul><ul><ul><li>API  でやるべきロジックを記述する </li></ul></ul><ul><ul><li>DataSource  へのアクセスロ...
Model (2) <ul><li>API Service (1) </li></ul><ul><ul><li>特にリクエスト毎にインスタンス化するような設計ではないので  Object::Container  に突っ込んでおく </li></...
Model (3) <ul><li>DataSource Service (1) </li></ul><ul><ul><li>DB (MySQL)  や  Memcached  などへのアクセス </li></ul></ul><ul><ul><...
Model (4) <ul><li>DataSource Service (2) </li></ul><ul><ul><li>Memcached  は接続情報が  Memcached  にあると言うモバゲーの流儀に従っている </li></ul...
DBIx::DBHResolver (1) <ul><li>大量にある  DB  へのハンドルを管理する </li></ul><ul><ul><li>Written by Kosuke Arisawa (DeNA) </li></ul></ul...
DBIx::DBHResolver (2) <ul><li>use  DBIx::DBHResolver;  </li></ul><ul><li>my   $r  = DBIx::DBHResolver-> new ;  </li></ul><...
DBIx::DBHResolver (3) <ul><li>connect_info </li></ul><ul><ul><li>各サーバーの接続情報をノード名をキーとして記述する </li></ul></ul><ul><ul><ul><li>...
DBIx::DBHResolver (3) <ul><li>use  DBIx::DBHResolver;  </li></ul><ul><li>my   $r  = DBIx::DBHResolver-> new ;  </li></ul><...
DBIx::DBHResolver (4) <ul><li>DBIx::DBHReslver  での  Sharding  の実施 </li></ul><ul><ul><li>あらかじめ何でどう分割するか決めておく </li></ul></ul...
DBIx::DBHResolver (5) <ul><li>use  DBIx::DBHResolver;  </li></ul><ul><li>my   $resolver  = DBIx::DBHResolver-> new ;  </li...
DBIx::DBHResolver (6) <ul><li>resolve_node_keys()  による  key  の分類 </li></ul><ul><ul><li>Activity Streams  の処理などで使ってます </li>...
DBIx::Connector (1) <ul><li>DBI  の薄い  wrapper </li></ul><ul><ul><li>シンプルなインターフェース </li></ul></ul><ul><ul><ul><li>transacti...
DBIx::Connector (2) <ul><li>use  DBIx::Connector;  </li></ul><ul><li>use  Try::Tiny;  </li></ul><ul><li>my   $conn  = DBIx...
SQL::Abstract::* (1) <ul><li>SQL  は原則  SQL::Abstract  で記述する </li></ul><ul><ul><li>但し複雑な  JOIN  や  GROUP BY  だとかはそのままヒアドキュメ...
SQL::Abstract::* (2) <ul><li>LIMIT, OFFSET  を使いたい </li></ul><ul><ul><li>SQL::Abstract::Limit  で出来るよ! </li></ul></ul><ul><l...
MySQL (1) <ul><li>MySQL Partitioning  の利用  (1) </li></ul><ul><ul><li>主に日時による  Range Partitioning  を利用しています </li></ul></ul>...
MySQL (2) <ul><li>MySQL Partitioning  の利用  (2) </li></ul><ul><ul><li>EXPLAIN PARTITIONS SELECT … </li></ul></ul><ul><ul><u...
MySQL (3) <ul><li>TRIGGER  の積極的な利用 </li></ul><ul><ul><li>TRIGGER  のメリットは大体デメリットだったりしますけど </li></ul></ul><ul><ul><ul><li>アプ...
MySQL (4) <ul><li>TRIGGER  とスキーマ変更 </li></ul><ul><ul><li>例えば  Range Partitioning  されていない複数のテーブルがある </li></ul></ul><ul><ul>...
MySQL (5) <ul><li>TRIGGER + Q4M でテーブルを自在に変更 </li></ul><ul><ul><li>これはまだ検証中です! </li></ul></ul><ul><ul><li>特定の slave に Q4M を...
MySQL (6) Old People Old Friend People Queue Friend Queue New People New Friend People Replication Worker (Perl) Friend Re...
3. Inside Social Activity with Q4M Q4M を利用した 大規模メッセージングシステム
Q4M usage <ul><li>使い方はとてもシンプル </li></ul><ul><ul><li>SELECT * FROM table_name WHERE queue_wait( ‘table_name’, wait_time ); ...
Worker (1) <ul><li>Q4M  や  Gearman  を使った  worker  の実装 </li></ul><ul><ul><li>Parallel::Prefork  を使えば簡単に書けます </li></ul></ul>...
Worker (2) <ul><li>Worker  の設定と配置 </li></ul><ul><ul><li>原則どのサーバーにも同じソースコードと設定ファイルが  deploy  されてます </li></ul></ul><ul><ul><...
Scaling Q4M (1) <ul><li>enqueue  が凄まじい環境での  Q4M </li></ul><ul><ul><li>enqueue  される順序が重要でない場合は、同じ用途の  Q4M  を複数台用意して、 Round ...
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) <ul><li>queue_wait()  する  worker  からの接続先も  DNSRR  すると、ゆらぎが収まらない場合がある </li></ul><ul><ul><li>worker  は特定のサーバ...
Other topics <ul><li>最近の事例 </li></ul><ul><ul><li>最新データを元に  memcached  にキャッシュを作りに行く処理が重たい </li></ul></ul><ul><ul><ul><li>やた...
4. Conclusion まとめ
Conclusion <ul><li>Shindig  を使った  OpenSocial  コンテナ </li></ul><ul><ul><li>そこまでびっくりするほど大変じゃないです </li></ul></ul><ul><ul><li>但...
Thanks & Questions? <ul><li>ご清聴ありがとうございました </li></ul><ul><li>時間余ってたら質問? </li></ul>
Upcoming SlideShare
Loading in …5
×

Inside mobage platform

6,704 views

Published on

Inside Mobage Platform Architecture

Published in: Technology
0 Comments
13 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
6,704
On SlideShare
0
From Embeds
0
Number of Embeds
585
Actions
Shares
0
Downloads
77
Comments
0
Likes
13
Embeds 0
No embeds

No notes for slide

Inside mobage platform

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

×