Share
Embed
Rails api way in aiming
「ゲーム開発の【ごっついい感じ】 ~ ソーシャルゲーム開発で使える 『ゲームAPI開発』 ~ ビヨンド勉強会 #7」での発表資料です。
https://beyond.doorkeeper.jp/events/56002
Yasutomo Uemori PRO
February 08, 2017
Transcript
51 slides
-
自己紹介 名前: 植森 康友 役職: ソフトウェアエンジニア 経歴: 2年目 担当: WebAPI
開発 管理ツー ル開発 インフラ?( ミドルウェア~) ansible Docker td‑agent bigquery 2 -
採用技術の選定: 各プロジェクトが適切だと思ったものを選ぶ プロジェクトメンバー の技術スタックがさまざま C#, C++, Ruby/Rails, PHP... 外部委託をする/ しない
開発するゲー ムの方向性 アクションがメインなのかRPG がメインなのか リアルタイムに通信をするのがどこからどこまでなのか プロジェクトごとの事情 納期 7 -
採用されやすい技術/ 言語 クライアント C#(Unity): 新規開発はほぼUnity C++(cocos‑2dx)、 ブラウザ: 剣と魔法のログレスなど リアルタイム C++:
昔からPC ゲー ムなどを作ってきたエンジニアが多数在 籍している C#: クライアントと処理を共通化しやすいため、 一部プロジ ェクトが採用 WebAPI Ruby(Rails): 新規開発の多くはほぼRails Python(Flask): 剣と魔法のログレスなど 8 -
リアルタイムサー バの役割はどこま で? チャットは何で通信してる? クライアントはどのデー タをどこまで キャッシュしてる? 見た目情報はクライアントが直接 WebAPI から取得する?
リアルタイ ムサー バが教える? 考える事多すぎ\(^o^)/ A. 要件による 14 -
リアルタイムサー バ エリアチャット キャラクタの位置と見た目 チャンネル情報 WebAPI サー バ 自分のレベル、 ジョブ、
所有ポイン ト、 見た目情報などはログイン情報と してWebAPI から 剣と魔法のログレスの場合 15 -
この画面で欲しい情報 キャラクター のステー タス キャラクター の装備アイテム キャラクター の見た目情報 各種装備のレベル アイテム所持数
考えたいこと 全武器の情報はこの時点で必要? 他のタブの情報はこの時点で必要? 装備が変更されたらいつ送る? ステー タスはどっちで計算する? 例) 装備画面を表示する場合 16 -
MMORPG のWebAPI 開発のポイント リアルタイム/WebAPI サー バ/ クライアントそれぞれがどこまで やるのか線引をする 一つの責務はなるべくどちらか片方のサー バだけが持つ
なるべく通信頻度/ 通信量を減らす できるだけ一度の通信で取ってこれるようにAPI を設計する その通信でどこまで取ってくるかはクライアントと相談 「 使う側が使いやすいAPI」 を設計する 一番工数がかかるのはクライアント側 WebAPI 側の仕様に合わせてクライアント側が頑張らなくて良 いようにする 17 -
Aiming におけるWebAPI の実装スタイル RPC スタイル REST API ではなく、RPC スタイルを採用 必要な情報群と1
1 になるエンドポイントを作る コー ドジェネレー タの利用 メッセー ジの型と形式を定義するファイル 定義ファイルから必要なファイルを自動生成する 19 -
RPC スタイルなRails ‑ routes G E T / v 1
/ s a m p l e / g e t P O S T / v 1 / s a m p l e / c r e a t e P O S T / v 1 / s a m p l e / u p d a t e P O S T / v 1 / s a m p l e / d e l e t e P O S T / v 1 / s a m p l e / n a m e / u p d a t e G E T / v 1 / c h a r a c t e r / g e t G E T / v 1 / l o g i n _ c h a r a c t e r / g e t HTTP メソッドはPOST とGET のみ GET 不要説も リソー ス名/ 動詞 をエンドポイントとしてルー ティング ネストもOK 22 -
RPC スタイルなRails ‑ controller m o d u l e
V 1 m o d u l e S a m p l e c l a s s G e t C o n t r o l l e r r e s c u e _ f r o m ( A c t i v e R e c o r d : : R e c o r d N o t F o u n d ) d o r e n d e r _ e r r o r ( : r e c o r d _ n o t _ f o u n d ) e n d d e f s e r v i c e s a m p l e = S a m p l e . f i n d ( p a r a m s [ : c h a r a c t e r _ i d ] ) r e n d e r _ a p i ( s a m p l e : s a m p l e ) e n d e n d e n d e n d コントロー ラはルー ティングと1 1 コントロー ラはservice アクションだけを持つという規約 render_api, render_error などrender をラップした専用メソッドでレ スポンスを送る 23 -
なぜRPC スタイルなのか クライアント→WebAPI の部分はラップして抽象化されている どうせURL もメソッドも利用側から見えていない リソー ス指向のメリットが薄い G E
T / v 1 / l o g i n _ c h a r a c t e r みたいなのが大量に作られる とある場面でだけ必要なオー ルインワンな取得系API 開発中は要件が変わりやすいので柔軟に対応する必要がある 仕様変更時にURL が変わらなくても良いようにしたい… Unity 側(WWW クラス) の仕様の問題 POST とGET にしか対応してなかった GET 時のクエリストリングを自前で組み立てる必要があった Unity5.4 から提供されるUnityWebRequest クラスでかなりマ シにはなった 24 -
IDL( インター フェー ス記述言語) WebAPI ⇔ リアルタイムサー バー・ クライアント間の通信プロトコ ルを定義するもの
言語間の型の違いなどを吸収する 定義から実装に必要なコー ドを自動生成する 26 -
プロトコル定義の目的 クライアント・ リアルタイムサー バ・API サー バでの設定の共有 実装が独自に進んでいく危険性を防ぐ 破壊的変更が入ったときにCI で検知する ドキュメントの生成がより用意になる
クライアントに対する mock サー バー、 あるいは サー バー に対する テスト用クライアントの自動生成がより容易になる 27 -
IDL の実装 ruby 製のパー サー( コマンドラインツー ル) パー スした内容はツリー 形式の情報になっている
Plugin 形式を採用 パー スした内容を受け取って、template を評価してコー ドを生 成する template はerb で記述 専用のDSL で記述された内容を読み込んで、 設定された内容を出力 する 28 -
通信プロトコルの定義 r p c ( : s a m p
l e _ c r e a t e ) { u r l ' / s a m p l e / c r e a t e ' m e t h o d ' P O S T ' d e s c r i p t i o n ' サンプルを作るよ' e r r o r s { i t e m : r e c o r d _ n o t _ u n i q u e , ' 名前が重複したよ' } r e q u e s t { p a r a m ' n a m e ' , : s t r i n g , ' 素敵な名前' } r e s p o n s e { p a r a m ' s a m p l e ' , : s a m p l e , ' 作成したさんぷる' } } 29 -
通信プロトコルの定義( エンドポイント) r p c ( : s a m
p l e _ c r e a t e ) { u r l ' / s a m p l e / c r e a t e ' # U R L のエンドポイントを指定 m e t h o d ' P O S T ' # H T T P M e t h o d を指定 d e s c r i p t i o n ' サンプルを作るよ' # R P C の説明 } R a i l s . a p p l i c a t i o n . r o u t e s . d r a w d o n a m e s p a c e : v 1 d o n a m e s p a c e : s a m p l e d o n a m e s p a c e : c r e a t e d o m a t c h ' c r e a t e ' , c o n t r o l l e r : : c r e a t e , a c t i o n : : s e r v i c e , v i a : : p o s t e n d e n d e n d e n d 30 -
通信プロトコルの定義( エラー) r p c ( : s a m
p l e _ c r e a t e ) { e r r o r s { # エラー の名前と説明 i t e m : r e c o r d _ n o t _ u n i q u e , ' 名前が重複したよ' } } m o d u l e S a m p l e m o d u l e C r e a t e m o d u l e E r r o r # e r r o r _ c o d e : 1 0 0 1 # d e s c r i p t i o n : 名前が重複したよ c l a s s R e c o r d N o t U n i q u e < : : R p c E r r o r : : R p c E r r o r B a s e d e f s e l f . e r r o r _ c o d e 1 0 0 1 e n d e n d e n d e n d e n d 31 -
通信プロトコルの定義( メッセー ジの形式) r p c ( : s a
m p l e _ c r e a t e ) { # リクエストの定義 r e q u e s t { # キー 名、 型、 説明 p a r a m ' n a m e ' , : s t r i n g , ' 素敵な名前' } # レスポンスの定義 r e s p o n s e { p a r a m ' s a m p l e ' , : s a m p l e , ' 作成したさんぷる' } } c l a s s R e q u e s t T y p e < T y p e : : B a s e a t t r i b u t e : n a m e , S t r i n g e n d c l a s s R e s p o n s e T y p e < T y p e : : B a s e a t t r i b u t e : s a m p l e , T y p e : : S a m p l e e n d 32 -
通信プロトコルの定義( 型定義) # 型定義 t y p e ( :
s a m p l e ) { p a r a m ' i d ' , : i n t , ' I D ' p a r a m ' n a m e ' , : s t r i n g , ' 名前' } r p c ( : s a m p l e _ c r e a t e ) { r e s p o n s e { # 利用例 p a r a m ' s a m p l e ' , : s a m p l e , ' 作成したさんぷる' } } c l a s s T y p e : : S a m p l e < T y p e : : B a s e a t t r i b u t e : i d , I n t e g e r a t t r i b u t e : n a m e , S t r i n g e n d c l a s s R e s p o n s e T y p e < T y p e : : B a s e a t t r i b u t e : s a m p l e , T y p e : : S a m p l e e n d 33 -
ここまでのコー ドをすべて自動生成する routing, controller, type class などをすべて自動生成 拡張しているプロジェクトでは以下のようなものも テストコー ド
(request spec) fixture (factory_girl) service class / service class spec 34 -
Rails 側の実装例 d e f p a r a m
s # パラメー タの検証を行い、 失敗したらエラー を発生させる # S t r o n g P a r a m e t e r とは違い、 型など厳密なチェックを行なう R e q u e s t T y p e . n e w ( s u p e r ) e n d d e f s e r v i c e s a m p l e = S a m p l e . c r e a t e ! ( n a m e : p a r a m s [ : n a m e ] ) r e n d e r _ a p i ( s a m p l e : T y p e : : S a m p l e . n e w ( i d : s a m p l e . i d , n a m e : s a m p l e . n a m e ) ) e n d 36 -
Rails 側の実装例 d e f r e n d e
r _ a p i ( r e s p o n s e ) # パラメー タの検証を行い、 失敗したらエラー を発生させる b o d y = R e s p o n s e T y p e . n e w ( r e s p o n s e ) r e s p o n d _ t o d o | f o r m a t | f o r m a t . m s g p a c k d o r e n d e r ( c o n t e n t _ t y p e : ' a p p l i c a t i o n / x ‐ m s g p a c k ' , b o d y : b o d y . t o _ m s g p a c k ) e n d # 開発時のみJ S O N を許可する事もできる f o r m a t . j s o n d o r e n d e r j s o n : b o d y . t o _ j s o n e n d i f R a i l s . d e v e l o p m e n t ? e n d e n d 37 -
Client 側の実装例 p r i v a t e v
o i d S e n d S a m p l e C r e a t e R e q u e s t ( s t r i n g n a m e ) { S a m p l e C r e a t e R e q u e s t r e q u e s t = n e w S a m p l e C r e a t e R e q u e s t ( ) ; r e q u e s t . n a m e = n a m e ; / / U R L , メソッドなどがラップされた、 自動生成されるクラス S a m p l e C r e a t e a p i = n e w S a m p l e C r e a t e ( r e q u e s t ) ; / / コー ルバックの登録 a p i . o n C o m p l e t e = O n S a m p l e C r e a t e C o m p l e t e ; a p i . o n E r r o r = O n S a m p l e C r e a t e E r r o r ; W e b R e q u e s t e r . R e q u e s t ( a p i ) } 38 -
型の違いの吸収 コー ドジェネレー タが生成するファイルで吸収する t y p e ( :
s a m p l e ) { p a r a m : i d , : i n t 6 4 } c l a s s T y p e : : S a m p l e a t t r i b u t e : i d , I n t e g e r # r u b y 側はI n t e g e r として扱う e n d c l a s s S a m p l e { p u b l i c l o n g i d ; / / C # 側はl o n g として扱う } s t r u c t S a m p l e { i n t 6 4 _ t i d ; / / C + + 側はi n t 6 4 として扱う } 39 -
迫りくるマイクロサー ビスの波 以前: いくつかのプロジェクトでリアルタイムサー バが巨大化しす ぎていた リアルタイムサー バがほとんどなんでもやる WebAPI は必要最小限のサブシステム
現在: 各々 が得意なことを役割分担する方向へ自然と移行 パフォー マンスが必要なものはリアルタイムサー バ パフォー マンスが必要ないものはWebAPI サー バ 41 -
Pros: エコシステムが充実している Rack middleware, Gem などのエコシステム 難しい実装でも既存gem を使う、 あるいは参考にして実行すれ ば実装が楽に終わる
ex) DB の水平分割など Rails 自体のエコシステム 長く使われてきたフレー ムワー クだけあって、rails 自体にはほ とんど手を入れる必要なくやりたい実装が出来る 「 こんな機能あったのか」 となることも少なくない 43 -
Pros: アー キテクチャが枯れている 実装面のベストプラクティスが豊富 fat controller, fat model などの問題を経て設計パター ンが成熟
している 社内に経験豊富なエンジニアがいるというのも強み 44 -
Pros: 管理ツー ルの実装が楽 ゲー ム開発では運営・ 管理を行うためのツー ルが必須 例) ギフトの配布、 ユー
ザサポー ト、KPI 分析など API サー バがRails で作られているため、 管理ツー ル側でmodel やモ ジュー ルなどを流用しやすい 45 -
Cons: Rails エンジニアの数が少ない 正確には「 ゲー ム業界に来るRails エンジニア」 の数が少ない 管理ツー ルなどのWeb
アプリケー ションから共通基盤、WebAPI と 幅広い領域をカバー するだけの人が足りていないのが現状 47