• Save
スマートフォン向けサービスにおけるサーバサイド設計入門
Upcoming SlideShare
Loading in...5
×
 

スマートフォン向けサービスにおけるサーバサイド設計入門

on

  • 10,234 views

 

Statistics

Views

Total Views
10,234
Slideshare-icon Views on SlideShare
9,292
Embed Views
942

Actions

Likes
31
Downloads
0
Comments
0

11 Embeds 942

http://yapcasia.org 573
http://blog.hatak.net 334
http://s.deeeki.com 18
https://twitter.com 5
http://localhost 4
http://192.168.33.10 2
http://slideshare-download.seesaa.net 2
https://si0.twimg.com 1
http://webcache.googleusercontent.com 1
https://www.google.co.jp 1
http://feedly.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    スマートフォン向けサービスにおけるサーバサイド設計入門 スマートフォン向けサービスにおけるサーバサイド設計入門 Presentation Transcript

    • スマートフォン向けサービスにおける サーバサイド設計入門 2012/09/28 hatak at YAPC::Asia Tokyo 2012
    • 自己紹介• hatak (HATAKEYAMA Hisashi) @hisashi hatak• ネット広告会社 → ソーシャルゲーム会社 • インフラや運用寄りの仕事をしてることが多いです • わりとなんでも• インフラ / MySQL / Perl な場所に時々います
    • このトークの内容• 今年春より新規サービスの立ち上げを行うことに • 元々インフラや運用がメインだったので、いろいろ試行錯誤することに• このサービスを実例として紹介しながら説明 • をしようと思ったのですが、まだリリースされていないため• 代替の例としてシンプルな SNS を作る流れで整理・説明します• 概説的というか、Perl に限らない話も出てきます • 今まで試行錯誤してみた話を整理します • むしろ「開発初心者が設計に入門してみました」という話かも。。• TMTOETDI
    • アジェンダ• 設計 • サービスを作るための最初をどうやって考えていくか• 開発 • サーバサイドをどのように作っていったか• 連携 • アプリとサーバを連携させるのにどうしたか
    • 設計 Design
    • 今回考えるサービスの概要• 『スマートフォン用専用アプリで利用する SNS』 を例として整理します • 現在開発中のサービスとは異なります。。• LiteSNS • iOS (iPhone) アプリで利用 • 利用にはユーザ登録が必要 • 機能 • メッセージや写真の投稿ができる • 投稿にコメントやいいねがつけられる • 友達のフィードが見れる
    • そもそもアプリでなければならない?• アプリにしなければならないか、を最初に整理してみる • ブラウザで閲覧するタイプのサービスではダメなのか? • 例) カメラやGPSなどの利用の有無• 機能ごとに処理を行うのがサーバかクライアントかで考えてみる • データを保存する場所は? • 投稿データの編集は? • 例) 画像加工(リサイズ, 減色, フィルタなど) • SNS へのログイン・データ取得は? • 例) facebook にログインしてトークンを取得
    • ネイティブとブラウザベース• それぞれのメリット・デメリットを踏まえておく • ネイティブアプリが優位な点 • カメラやセンサーなどの利用が容易 • アプリマーケットからの導線が期待できる • リッチなユーザ体験を実現しやすい • ブラウザベースのアプリが優位な点 • アプリアップデート時の作業・影響が少ない • システム構成をシンプルに保ちやすい• その上で特性を十分に活かせるようにした方がよい
    • 見える側から裏側へと考えていく• 作業に着手するのもこの順になる • 作りながら、互いに連携して軌道修正していく必要もある
    • 画面 / UI を考える• 先人に学ぶのがいいと思う • 自分で触ってみるのが一番• OS のガイドラインもあるので参考にしてみる • iOS Human Interface Guidelines • Android Design ( http://developer.android.com/design/ )• 様々な書籍も出版されている
    • WebAPI を考える• method + param/body → response を考える • REST の考え方を取り入れつつ、使いやすい URL を考える • ブラウザと異なり PUT / DELETE などのメソッドも使える• 他のサービスがどのように作っているかを調べてみる • 既存のサービスの API (facebook, Twitter, Google+ など) • フレームワークの思想 (Catalyst, Ruby on Rails)
    • サーバサイドを考える• アプリケーションサーバは並列に追加できるように• 用途に応じて複数のデータストアを用意 • データベースサーバ (MySQL) • 永続的に保存するデータ • KVS (Redis) • 永続的に保存するデータ • DB よりもアクセス/更新などの頻度が高いもの • キャッシュ (memcached) • ローカルなキャッシュ(消えてもいい) • app サーバ毎に保持
    • LoadBalancer App App App App Nginx Nginx Nginx Nginx Starman Starman Starman Starmanmemcached memcached memcached memcached LoadBalancer LoadBalancer DB Master KVS Master MySQL DB Slave Redis KVS Slave DB Slave KVS Slave MySQL Redis MySQL Redis
    • ユーザ管理• セッションは Cookie で管理 • OAuth なども考えたが、まずは非公開 API なのでシンプルに • スマートフォンアプリ側のライブラリも Cookie を処理してくれる• アクティブなセッションが把握できる仕組みが必要 • アプリ削除でそのまま接続できなくなったセッションの処理 • PUSH 通知を行っても良いアクティブなセッションの把握 • 古いセッションの Expire が必要な場合もある • アクティブなセッションを確認する機能
    • 開発 Development
    • Perl での開発• Perl を選んだ理由 • 比較的勝手が分かる言語 • 今 を取り入れてそれなりの規模の開発をできるかどうか • CPAN のモジュール群の魅力• システムに依存しない環境$ curl  -­‐kL  https://raw.github.com/gist/2775862  |  bash • perlbrew • システムの Perl を直接使いたくない • Carton • ローカルの開発環境など複数のプロジェクトが同居するときに重宝
    • フレームワーク• Amon2 • 拡張が自由にできる 自分でやらなきゃいけない • 仕組みを考えていく上では柔軟性が高いと思い選択• 使ってみて • コード量も依存も少ないので困ったら読めばいい • 自分がやりやすい形に作り替えていける感じはとても良い • もともとの設計思想から外れてしまっているかも知れないが。。。
    • アプリケーションの全体構成• Amon2::Setup::Flavor::Large を利用してスケルトンを生成$ amon2-setup.pl --flavor=Large,Teng LiteSNS • これをベースとして構成を手直ししていく• LiteSNS の中で Amon2::Web を継承したコントローラ群を複数用意 LiteSNS::APIv1 --- API としてレスポンスを返す (JSON) LiteSNS::Admin --- ブラウザで閲覧する認証付き管理画面 (HTML)• 全体で共用するモジュールはコントローラと並列の階層 LiteSNS::Model --- ロジックをまとめたモジュール LiteSNS::DB --- データベースモジュール LiteSNS::Exceptions --- 例外モジュール
    • LiteSNS├── APIv1 API Module <Amon2::Web>│   ├── Controller Controller│   │   ├── Article│   │   ├── User│   │   └── ...│   ├── Dispatcher Dispatcher│   └── Response Response Model│      ├── Article│      ├── User│      └── ...├── Admin Admin-Web Module <Amon2::Web>│   ├── Controller│   └── Dispatcher├── Constants Constant (Common)├── DB│   └── Main Main Database <Teng>│      ├── Row│      │  ├── Articles│      │  ├── Users│   │  └── ...│      └── Schema├── Exceptions Exception <Class::Exception>├── Model Logic Model│   ├── Article│   ├── User│   └── ...└── Util UtilityMethods (Common)
    • LiteSNS::DB• ORM として Teng を利用 • あとでモデルでちゃんと SQL 整理すればいい • でも、インデックスなどをしっかり考えると整理はあった方がいい • 管理画面のようなケースはORMのままでもいいと思う• データセット毎に別のクラスとして用意 • 複数系統持つことになると思うので • データセットや master / slave をつなぎ替えられるように• 日付や定義値などのカラムは inflate / deflate 設定しておく
    • データの論理削除• 全テーブルにフラグ (row_status) を持たせておく 1 : enable 0 : disable -1 : deleted• DB クラスで Class::Method::Modifiers を使う • delete メソッドを置き換えてしまう
    • package LiteSNS::DB::Main;use parent qw(Teng);use Class::Method::Modifiers;use DateTime;use constant STATUS => { ENABLE => 1, # 有効 DISABLE => 0, # 無効 DELETED => -1, # 削除};around delete => sub { my ($orig, $self, $table_name, $delete_condition) = @_; my $update_row_data = { updated_at => DateTime->now(time_zone => Asia/Tokyo), row_status => STATUS()->{DELETED}, }; $self->update($table_name, $update_row_data, $delete_condition);};before single => sub { my ($self, $table_name, $search_condition, $search_attr) = @_; unless (defined $search_condition->{row_status}) { $search_condition->{row_status} = STATUS()->{ENABLE}; }};...
    • LiteSNS::Model• 共通の処理として呼べるようにコントローラから分離 • API だけでなく、Admin や CLI からも同じ処理をする可能性があるため • Mouse ベースでリクエストに依存しない処理をまとめたクラスを作成• 常に決まった処理をするようなロジックをまとめる • フローに沿ったステータスの遷移 • 例) 友達の状態をチェックして申請中 → 承認にステータスを変える • 合計数のインクリメント/デクリメント • 例) コメントを投稿したら、その記事のコメント数をインクリメント
    • LiteSNS::APIv1::Response• レスポンスを詰め込むオブジェクトを用意 • 入れ子構造 • 不要カラムの削除や属性名の変更など• Teng::Row を詰め込んで JSON で出力したいハッシュに変換・整形 • 最初は Teng::Row に変換メソッドを組み込んでみた • テーブルとモデルが必ずしも一対一対応しなかったので分離
    • users { id => 928, id user => { id => 1001, name name => ‘taro’, }, passwordarticles title => ‘今日のランチ’, article_count body => ‘パンがおいしかった!’, id createdAt => ‘2012-09-28 13:24:01’, row_status comments => [ user_id { created_at user => { title updated_at id => 1034, body name => ‘jiro’, }, comment_count body => ‘おいしそうだね!’, like_count createdAt => ‘2012-09-28 13:31:34’, }, row_status comments { user => { created_at id id => 1001, updated_at user_id name => ‘taro’, }, article_id body => ‘焼きたてをだしてくれるん だー’, body createdAt => ‘2012-09-28 13:35:18’, row_status }, ], created_at }, updated_at
    • JSON レスポンスの生成• 標準プラグイン Amon2::Plugin::Web::JSON を利用 • 今回はレスポンスコードを変えたかったので一部変更 • Controller からの戻り値でレスポンスコードを渡せるように• JSON / JSON::XS • PERL -> JSON では数字の取り扱いに注意 • 類推してくれるが、明示的に数値になっていないと数値にならない
    • エラーと例外の処理• Class::Exception を利用 • アプリケーション共通で利用する例外クラスを作成 • 例外が発生した場合にこのインスタンスを投げる• Dispatcher / Worker のレベルで Exception をキャッチする • 該当する例外の内容に合わせてエラーメッセージを生成 • HTTP ステータスコードに割り当ててレスポンスとして作成
    • テスト• Model や共通関数などはテストを作っておく • 実際に処理させてチェックだと面倒 • テストに合わせて daemon を起動するようにしてあげると良い • http://perl-users.jp/articles/advent-calendar/2011/test/18• テストを書きすぎるとキリが無くなってしまうのでバランス大事 • Jenkins などの CI ツールをうまく活用• API はレスポンスのテストしやすい • JSON なので期待値との比較などが容易 • Test::WWW::Mechanize::PSGI などを活用
    • 環境ごとの切り分け• Amon2 の機能で config を切り替えられる • 接続先ホストやユーザ名などを変えておく • 開発時だけ環境変数でフラグを入れたりすると便利• 本番と開発環境でドメイン + 階層が異なってもうまく吸収できる • 同一ドメインで全てをサーブしたい場合はまとめた psgi を作ってたてる# app.psgi...builder { mount /api/ => Plack::Util::load_psgi(api.psgi); mount /admin/ => Plack::Util::load_psgi(admin.psgi);}; • 異なるドメインの場合は個別にプロセスをたてる • Proxy などでわけてアクセスできるようにする
    • 本番環境• Starman + Server::Starter$ carton  exec  -­‐-­‐  start_server  -­‐-­‐port=5000  -­‐-­‐signal-­‐on-­‐hup=USR1  -­‐-­‐  plackup  -­‐s  Starlet  -­‐-­‐spawn-­‐interval=5 • プロセスを supervisord で立ち上げる • 標準出力のログは supervisord がファイルに書き出してくれる• ローカルに Nginx をたててローカルの Starman に送る • Nginx 側でアクセスログを取得 • 管理画面のアクセス制御や多段 Proxy 時のヘッダコントロールなど
    • 連携 Cooperation
    • クライアントアプリとの連携• iPhone を例にいくつか紹介 • Objective-C 的な話も出てきますが軽く触れるだけにします• AFNetworking (https://github.com/AFNetworking/AFNetworking) • AFJSONRequestOperation で JSON -> NSDirectory と変換 • NSDirectory で init できるようにデータオブジェクトを定義しておく • オブジェクトが入れ子になっているようにしておく • このクラスがあるから、API の出力も同じようにできるオブジェクトを考 えてるわけで
    • API のコールと JSON 値の変換• 1回のリクエストで階層化したデータを返せるようにする • 通信回数を抑えることが大事 • 画面遷移の度に通信を発生させたくない • 海外を見据えてるなら通信環境が優れている場所ばかりとは限らないため• 値を変更したらその変更を通知して画面も更新する必要がある• インターフェイスとのバランスも考える必要がある • 送信 ボタンがあるとは限らず、トリガスイッチだけだったりもする• アプリ開発者と連携しながら相互に調整する必要がある
    • リクエストヘッダ• 独自のユーザエージェントを設定• 独自の HTTP ヘッダをアプリからの通信時に埋め込む • クライアントアプリのバージョン • X-LiteSNS-Version • ユーザの閲覧環境 • X-LiteSNS-Locale • X-LiteSNS-Language • X-LiteSNS-Timezone• このあたりはアプリサイドのコードで埋め込むことができる • ルールを決めておくことが大事
    • APNS を用いた PUSH 通知• Apple Push Notification Service (APNS) • 詳細は iOS Developer Library を参照• 通知の流れ 1.アプリでデバイストークンを取得してサーバに送信 • NSData を16進数などに変換してサーバに POST 2.サーバサイドでアクティブなセッションと紐づけて保持しておく 3.モジュールを利用して通知パケットを APNS サーバに送る • Net::APNS, AnyEvent::APNS• AnyEvent::APNS の例は typester さんの解説が分かりやすいです • http://perl-users.jp/articles/advent-calendar/2010/hacker/7
    • -­‐(void)applicationDidFinishLaunching:(UIApplication  *)application  {                //  初回起動時に  「“LiteSNS”はあなたにプッシュ通知を..」  という確認ダイアログを表示        [[UIApplication  sharedApplication]  registerForRemoteNotificationTypes: (UIRemoteNotificationTypeAlert  |  UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];}-­‐  (void)application:(UIApplication  *)app  didRegisterForRemoteNotificationsWithDeviceToken:(NSData  *)deviceToken  {          //  ここで  NSData  形式の  deviceToken  を16進数変換して送ります}use Net::APNS;my $APNS = Net::APNS->new;my $notification = $APNS->notify({ cert => ‘path/to/app.cer’, key => ‘path/to/app.key’, });$notification->devicetoken(‘device_token’); # ここで16進数のトークンをセットする$notification->message("new message from LiteSNS"); # 通知メッセージ本文$notification->badge(1); # アプリアイコンにつけるバッジの数$notification->write;
    • まとめ Summary
    • まとめ• 設計 • 見えるところから見えないところへと考えを掘り下げていく • 最初に全てを決めきれないので、作りながら微調整は必要• 開発 • 仕組みを考えながら組み合わせて作っていく • スケールする仕組みをあとからでも入れられるようにする• 連携 • ライブラリをうまく使ってデータを組み立てていく • スマートフォンならでは、アプリならではの視点も必要
    • まとめ• やってみて分かったこと • 様々な観点が必要とされている • プログラム、ミドルウェア、に加えてフロント側も • 特性とかユーザの利用シーンもちゃんと考えないとうまく作れない • でも、難しいことではない • (本来は)全部を一人でできなくてもいい • タスクを振るとき、全体を設計するときにあると良い • 何ができるか、を知っていることが大事 • Titanium など敷居の低い開発手法もあるので試してみるのが良い
    • ご清聴ありがとうございました!