Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

脱RESTful API設計の提案

8,690 views

Published on

RESTful APIであることにこだわらなければ皆(主に開発者)がハッピーになれるかもしれません。

Published in: Engineering
  • Be the first to comment

脱RESTful API設計の提案

  1. 1. 脱RESTful API設計手法の提案 株式会社ゆめみ 仲川樽八
  2. 2. はじめに この資料は、アプリの開発にあたって、どの機能をアプリで提供し、どの機能はAPIサーバで提供 するべきか、そしてそのAPI設計の粒度をどうするべきかという問題に対する回答の一つを示すも のです。 この資料においてはソーシャルゲームでの利用を例としています。 corporate data center フロントアプリ ・ブラウザ APIサーバ 通信 RESTful APIがよく利用される
  3. 3. フロントアプリ スタンドアロンゲームの構成要素(例) グラフィック アニメーション サウンド レベルアップ戦闘ルール 評価・報酬 シナリオ ゲームシステム・基本ルール 演出 セーブ・ロード 全ての機能はフロントアプリに実装される
  4. 4. ソーシャルゲームになって構成要素が増えた 各種 マスタリソース 追加シナリオ イベント配信 アカウント 引き継ぎ 会員登録 多端末 同時利用 運営から アカウント凍結 アプリ上データ アカウント関連機能 会員 セーブデータ データ管理機能 各種ログ管理 フレンド 登録管理 (準)課金機能 その他運営機能 連続ログイン 報酬 課金管理 アイテム ガチャ管理フレンド連携 報酬管理 報酬付与 グラフィック アニメーション サウンド レベルアップ戦闘ルール 評価・報酬 シナリオ ゲームシステム・基本ルール 演出 セーブ・ロード どの機能をゲームサーバに持たせるか?どう利用させるか?
  5. 5. ほぼ全ての機能をサーバサイドに持たせるとほとんどWebアプリになり、 ゲーム性を損ないやすい。また、サーバ負荷が増える。 逆にほぼ全ての機能をフロントアプリに持たせるとチートがしやすくなる。
  6. 6. ぶっちゃけ、これが決まらないと 何も作れないよね。
  7. 7. 原点に立ち戻って、 ・ユーザーの要求 ・アプリ開発者の要求 の両面から考える
  8. 8. ゲームなんだからレスポンスって重要だ。 対戦の命令を出したらあとは見てるだけのゲームより、ちゃ んと操作出来るゲームで遊びたい。 バックエンドの能力に引きずられて待たされたり、止まっちゃ うようなゲームなんて嫌だ。 ユーザーの欲求 →サーバAPIを使うことでゲーム体験の低下があるのは嫌だ。 (ブラウザゲームじゃちょっと、、、。)
  9. 9. アプリ開発側が、スタンドアローンゲーム開発で培ってきた 開発プロセスをできるだけ活かせるようにしたい。 ゲームロジックを持つAPIの実装をいちいち待たなくても開発 を継続できるようにしたい。 アプリ開発者の欲求 →サーバAPIを使うことで開発の邪魔をされなくない
  10. 10. ・サーバAPIとしては単機能のものを複 数準備する。 ・サーバ側は複雑なロジックを持たず に、複雑なゲームロジックはフロントア プリでサーバAPIを組み合わせながら 構築する。 ・同一のHTTPリクエストの中で複数の APIを同時実行できるようする。 サーバサイド設計者からの解決案の提示
  11. 11. 実装してみた 命名:T8エンジン
  12. 12. ゲームサーバ フロントアプリ 「アイテム購入API」を準備しないかわりに、 「課金アイテム消費API」 「アイテムGET API」 「現在の保有アイテム取得API」 を同時にコール出来るようにする。 T8エンジン 利用例 体力全回復 API課金アイテム消費API アイテムGET API 現在の保有アイテム取得 API 会員データ更新 API メッセージ送付 API アイテム購入 API × その他、単機能のAPI
  13. 13. ロジックの多くがフロントアプリに寄ることによって、通信状 況やサーバの状況に巻き込まれてユーザー体験が損なわ れる事が減る。 より、リッチで、インタラクティブなゲームをプレイできる。 ユーザーのメリット
  14. 14. ・サーバ上のAPIの組み合わせによって、サーバ上のリソースに自由 にアクセスできる。 ・仕様変更時もアプリ側の修正ですぐに対応可能。 ・サーバサイドの実装を待たずに自由に開発を継続する事ができる。 ・フロントにロジックがあるので、よりインタラクティブなゲームを自由 に構築する事ができる。 アプリ開発者のメリット
  15. 15. 本音 • サーバ側でゲームロジックの実装をすると面倒くさい。 • インタラクティブなゲームとなるとより面倒な結合が増える。 • サーバの通信回数も負荷もできるだけ抑えたい。 • ロジックがフロントによると利用ユーザーが増えても、サーバの負 荷が増えにくいので負荷設計が楽。 • 要件がよくわかんないから、イベントに対する膨大な組み合わせの APIの実装なんてやってられない。
  16. 16. ユーザーからの要望、アプリエンジニアから の要望に対応しているように見えて、自分た ちにとってハッピーな開発ができる。
  17. 17. ゲームサーバフロントアプリ サーバ側はどうしてもサーバでやらないと ならない部分の開発に注力できる。 各種 マスタリソース 追加シナリオ イベント配信 アカウント 引き継ぎ 会員登録 多端末 同時利用 運営から アカウント凍結 通信 アプリ上データ アカウント関連機能 会員 セーブデータ マスタデータ管理機能 データ管理機能 各種ログ管理 フレンド 登録管理 (準)課金機能 その他運営機能 連続ログイン 報酬 課金管理 アイテム ガチャ管理フレンド連携 報酬管理 報酬付与 グラ フィッ ク アニ メー ション サウン ド レベル アップ 戦闘 ルー ル 評価・ 報酬 シナリ オ ゲームシステム・基本ルール 演出 セー ブ・ ロード
  18. 18. メリットだらけに見える
  19. 19. デメリットは?
  20. 20. デメリット 1. 複数のAPIを同時コールできるようにするために、インタフェイスが RESTful APIでなくなる。 2. アプリや通信をクラックされた時にチートをされやすくなる。
  21. 21. デメリット1 複数のAPIを同時コールできるようにするために、 インタフェイスがRESTful APIでなくなる。 どうしてもRESTful APIである必要がある場合、間にプロクシサーバを立てる必要 がある。 同一のHTTP リクエストで投げる ゲームサーバ フロントアプリケーション プロクシサーバ 課金アイテムを消費して アイテムをGETして、 現在のアイテムを取得する API 課金アイテムを消費して アイテムをGETして、 現在のアイテムを取得したい RESTful APIとし て作る。 RESTful APIではない。 課金アイテム消費API アイテムGET API 現在の保有アイテム取得 API
  22. 22. ただし、ゲームのようにそもそも複数のリソースに対する参照、更新の操作が必 要な時点でRESTful APIとして設計・構築することが難しい。
  23. 23. デメリット2 アプリや通信をクラックされた時にチートをされやすくなる。 チートを防ぐための努力は必要だが、それでもチートはされるものだと考える。 チートをされた際の影響範囲の洗い出しと評価が必要。 全体のポリシーとしては、他人のデータを操作出来るチートは一発アウトとします。 チーター本人のデータの書き換えまではある程度覚悟して、一部のユーザーの チートが全体のバランスを崩さないようにするゲームデザインが必要となる。
  24. 24. 複数のAPIの同時実行について フロントアプリケーションでは、準備されたAPIのうちの任意のAPIを複数選んでロ ジックを組み立て、それらを同一のトランザクション処理内で実行する事ができま す。 それぞれのAPIは同一のトランザクション中で、指定された順番通りに実行されま す。 体力全回復 API 課金アイテム消費API 100コイン使え 同一のHTTP リクエストで投げる 保有コインから100コインを使ってゲームデータをセーブする ついでにおまけアイテムも渡す。 コインが不足していたらその旨を通知してロールバックする ゲームサーバフロントアプリケーション アイテムGET API 現在の保有アイテム取得 API
  25. 25. ここで、トランザクション処理とは?
  26. 26. トランザクション処理とは? DB等に対する複数の更新処理の一貫性を担保するための処理です。 いわゆるKVSにはこの処理ができないものも多いですので、組み合わせて利用する際には注意 が必要です。 ユーザーの所持金を100円減らせ 前の処理がOKだったら、ユーザーにアイテムを渡せ ←OK 減らしたよ 100円で復活アイテムを買う例 ここでクラッシュ ←OK 渡したよ
  27. 27. 部分的な処理に失敗した時であっても、全てのデータを元通りに直してくれます。 部分的な処理の失敗はHW、ネットワークやミドルウエアの問題である事が多く、開発者が手を出 せないケースも多いですので、これは神のようなシステムです。(※開発者の責任だったとしても ですが。。。) ユーザーの所持金を100円減らせ ←OK 減らしたよ 100円で復活アイテムを買う例 ここでクラッシュ 課金もなかったコトに しましょう! 前の処理がOKだったら、ユーザーにアイテムを渡せ
  28. 28. トランザクション処理が利用できないシステムだとどうなるか? 体力全回復 API 課金アイテム消費API 100コイン使え API実行 ゲームサーバフロントアプリケーション アイテムGET API 現在の保有アイテム取得 API ← OK API実行 ← OK API実行 ← OK API実行 体力減らす API 課金アイテム戻すAPI 100コインAPI API実行 途中で通信に失敗したら、それまでのAPI実行をキャ ンセルする為のAPIをコールしなければならない。 →どこまで成功したかを把握する必要がある。 →キャンセルAPIも失敗する可能性がある。 無理ゲー… もしNGだっ たら? キャンセルが NGだったら?
  29. 29. リリース後の ・課金アイテムロストバグ ・アイテム無限増殖バグ 等の発生源となります。 大変盛況なため、予期しなかったバグが発生しています。 稼働させながらバグを直すので、皆様協力して下さい。 (2017年のとあるソシャゲ公式発表の要約)
  30. 30. この状態を回避するための代替ソリューションもありますが・・・ ゲームサーバ上に、 ○コインを利用して ○体力を全回復して、 ○おまけのアイテムをゲットして、 ○その結果現在の保有アイテムを取得する という全ての操作行うAPIを準備する。 ・作らなければならないAPIの組み合わせが膨大すぎて死ねる。 ・当然、テストも膨大になる。 ・仕様変更に弱い。 デスマーチの予感!!
  31. 31. 単一のデータソースにおける トランザクション処理の担保方法
  32. 32. 前提: 対象となるデータソースがトランザク ション処理をサポートしている場合
  33. 33. try{ begin_transaction( Database1 ); データ処理1( Database1 ); データ処理2(Database1 ); データ処理3(Database1 ); commit_transaction(Database1 ); catch Exception(){ rollback(Database1) } 単一のデータソースにおけるトランザクション処理の担保方法 →データ処理1~3のどこで失敗しても、commit_transaction([to Database1] )が実行されないかぎり、 どんなデータ処理を行ってもそれらはロールバックされる。(データ処理を行う前の状態に戻る) ※rollbackを記載していなくても、commitされない限り、rollbackされる どんな例外でもとにかく元の状態に戻せ トランザクション開始宣言 トランザクション終了宣言
  34. 34. 複数のデータソースにまたがる トランザクション処理の担保方法
  35. 35. try{ begin_transaction( Database1 ); データ処理1(Database1 ); begin_transaction( Database2 ); データ処理2( Database2 ); データ処理3( Database1 ); commit_transaction( Database2 ); commit_transaction(Database1 ); catch Exception(){ rollback( Database2) rollback( Database1) } 複数のデータソースにまたがるトランザクション処理の担保方法 →begin_transactionとcomimt_transactionを入れ子にすることで、全体をトランザク ション処理することが可能。 トランザクション開始宣言 どんな例外でもとにかく全部もとに戻せ トランザクション終了宣言
  36. 36. →複数のデータソースをまたがる事も可能なの で、DBのシャーディング(水平分割)にも対応可能。 マスタDB ユーザーDB1 ユーザーDB2 ユーザーDB3 更新があったDBの みcommit 更新があったDBの みcommit ユーザーDB4
  37. 37. トランザクション処理を担保しない データソースに対する トランザクション処理方法
  38. 38. 一般的にトランザクション処理をサポートする データソースの更新処理性能はそうでないデー タソースと比較して低い事が多いため、高負荷を 前提とするシステムにおいては、トランザクション 処理をサポートしないKVS等のNoSQLを利用する 事も多い。 前提:
  39. 39. 1. リードスルーキャッシュ として利用している場合
  40. 40. try{ begin_transaction(Database1 ); データ処理1( Database1 ); データ処理2( Database1 ); データ処理3( KVS_Key1 ); commit_transaction( Database1 ); catch Exception(){ delete( KVS_Key1) rollback( Database1) } 複数のデータソースにまたがるトランザクション処理の担保方法 →ロールバック用のブロック中で、更新対象となったKVSのキーを削除すれば、次 回のリクエスト時に新たにデータが作成される。(疑似的なロールバック) トランザクション開始宣言 更新対象となったデータを消す トランザクション終了宣言 Database1に関しては更新前に戻す
  41. 41. →リードスルーキャッシュとしての範囲であれば、 NoSQL等を組み合わせることも可能。
  42. 42. 2. 永続的なデータとして 利用している場合
  43. 43. 単純に消せばいいというわけではなく、戻す先の状態を考えなければならないの で、トランザクション担保は基本的に非常に難しい。 ※元のデータを保存していても、ロック機構が無いことも多く、その状態に戻していいかどうかが 不明なため
  44. 44. 高負荷時や、システムのエラー時における ・課金アイテムロストバグ ・アイテム無限増殖バグ は避けられないので、せめてユーザーの不利益にはならない方向にシステムを 実装する。 ※課金アイテム→通常アイテムの交換に関して言うと、先に通常アイテムを付与 してから、課金アイテムを減らすなどをする。
  45. 45. T8 エンジンの具体的なコール方法
  46. 46. HTTPリクエスト例 curl - -X POST "${HOST}/${VERSION}/gameapi" -H "Content-type: application/json" -d " ${JSON_BODY}"; →一般的なHTTPリクエスト方式 但し、リクエストメソッドは全部POST
  47. 47. JSON_BODY部分 { “token” : “user_idとnonce, user_secret, 時刻等から生成した認証用token”, “user_id” : “user_id”, “nonce” : “ランダム文字列”, “body” : [ { “api_name” : “課金アイテム消費API”, “api_id” : “課金アイテム消費API_01”, “params” : {“use_num” : 100} }, { “api_name” : “アイテム付与API”, “api_id” : “アイテム付与API_01”, “params” : {“item_id”: 10, “num” : 5 } }, { “api_name” : “アイテム取得API”, “api_id” : “アイテム取得API_01”, “params” : {“item_id”: 10 } } ] } ※user_secretは予めサーバとアプリ 間で共有しておき、tokenを生成する ために使用されるが、通信ではやり 取りされない。 ※api_idはレスポンス中でapiを特定 するために利用するため、一意であ れば何でも良い。
  48. 48. HTTPレスポンス例(成功例) HTTP/1.1 200 OK { “result_code” : 1, “results” : [ { “api_id” : “課金アイテム消費API_01”, “result” : {“result”:”OK”} }, { “api_id” : “アイテム付与API_01”, “result” : {“result”: “OK” } }, { “api_id” : “アイテム取得API_01”, “result” : {“result”: “OK”, {“item_id”:10, “num”:15} } } ] } ※個別のAPI毎の実行結果がリス トで返答される。
  49. 49. HTTPレスポンス例(失敗例) HTTP/1.1 400 BadRequest { “result_code” : 0, “error” : { “api_id” : “課金アイテム消費API_01”, “err_code” : 1, “msg” : “残金が不足しています。チャージして下さい。” } }
  50. 50. 利用実績
  51. 51. ・ソーシャルゲームアプリ ・クーポン、情報提供アプリ ・予約サイトAPI など、ゲームだけでなく複数のアプリでの実績有り。 …気がついていないだけで使っていたかも?
  52. 52. 結論
  53. 53. RESTful APIであることにこだわらなければ みんな(主に開発者)がハッピーになれるかもしれません。

×