rails api way in aiming
TRANSCRIPT
自己紹介
名前:植森 康友役職:ソフトウェアエンジニア
経歴:2年目
担当:
WebAPI開発
管理ツール開発
インフラ?(ミドルウェア~)
ansible
Docker
td‑agent
bigquery
2
採用技術の選定: 各プロジェクトが適切だと思ったものを選ぶ
プロジェクトメンバーの技術スタックがさまざま
C#, C++, Ruby/Rails, PHP...
外部委託をする/しない
開発するゲームの方向性
アクションがメインなのかRPGがメインなのか
リアルタイムに通信をするのがどこからどこまでなのか
プロジェクトごとの事情
納期
7
採用されやすい技術/言語
クライアント
C#(Unity): 新規開発はほぼUnity
C++(cocos‑2dx)、ブラウザ: 剣と魔法のログレスなど
リアルタイム
C++:昔からPCゲームなどを作ってきたエンジニアが多数在
籍している
C#:クライアントと処理を共通化しやすいため、一部プロジ
ェクトが採用
WebAPIRuby(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
GET /v1/sample/getPOST /v1/sample/createPOST /v1/sample/updatePOST /v1/sample/deletePOST /v1/sample/name/updateGET /v1/character/getGET /v1/login_character/get
HTTPメソッドはPOSTとGETのみGET不要説も
リソース名/動詞 をエンドポイントとしてルーティング
ネストもOK
22
RPCスタイルなRails ‑ controller
module V1 module Sample class GetController rescue_from(ActiveRecord::RecordNotFound) do render_error(:record_not_found) end
def service sample = Sample.find(params[:character_id]) render_api(sample: sample) end end endend
コントローラはルーティングと1�1
コントローラはserviceアクションだけを持つという規約
render_api, render_errorなどrenderをラップした専用メソッドでレスポンスを送る 23
なぜRPCスタイルなのか
クライアント→WebAPIの部分はラップして抽象化されているどうせURLもメソッドも利用側から見えていない
リソース指向のメリットが薄い
GET /v1/login_character みたいなのが大量に作られるとある場面でだけ必要なオールインワンな取得系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
通信プロトコルの定義
rpc(:sample_create) { url '/sample/create' method 'POST'
description 'サンプルを作るよ'
errors { item :record_not_unique, '名前が重複したよ' }
request { param 'name', :string, '素敵な名前' }
response { param 'sample', :sample, '作成したさんぷる' }}
29
通信プロトコルの定義(エンドポイント)
rpc(:sample_create) { url '/sample/create' # URLのエンドポイントを指定 method 'POST' # HTTP Methodを指定
description 'サンプルを作るよ' # RPCの説明}
Rails.application.routes.draw do namespace :v1 do namespace :sample do namespace :create do match 'create', controller: :create, action: :service, via: :post end end endend
30
通信プロトコルの定義(エラー)
rpc(:sample_create) { errors { # エラーの名前と説明 item :record_not_unique, '名前が重複したよ' }}
module Sample module Create module Error # error_code: 1001 # description: 名前が重複したよ class RecordNotUnique < ::RpcError::RpcErrorBase def self.error_code 1001 end end end endend
31
通信プロトコルの定義(メッセージの形式)
rpc(:sample_create) { # リクエストの定義 request { # キー名、型、説明 param 'name', :string, '素敵な名前' }
# レスポンスの定義 response { param 'sample', :sample, '作成したさんぷる' }}
class RequestType < Type::Base attribute :name, Stringend
class ResponseType < Type::Base attribute :sample, Type::Sampleend
32
通信プロトコルの定義(型定義)
# 型定義type(:sample) { param 'id', :int, 'ID' param 'name', :string, '名前'}
rpc(:sample_create) { response { # 利用例 param 'sample', :sample, '作成したさんぷる' }}
class Type::Sample < Type::Base attribute :id, Integer attribute :name, Stringend
class ResponseType < Type::Base attribute :sample, Type::Sampleend
33
ここまでのコードをすべて自動生成する
routing, controller, type classなどをすべて自動生成
拡張しているプロジェクトでは以下のようなものも
テストコード (request spec)
fixture (factory_girl)
service class / service class spec
34
Rails側の実装例
def params # パラメータの検証を行い、失敗したらエラーを発生させる # StrongParameterとは違い、型など厳密なチェックを行なう RequestType.new(super)end
def service sample = Sample.create!(name: params[:name]) render_api(sample: Type::Sample.new( id: sample.id, name: sample.name ))end
36
Rails側の実装例
def render_api(response) # パラメータの検証を行い、失敗したらエラーを発生させる body = ResponseType.new(response) respond_to do |format| format.msgpack do render( content_type: 'application/x‐msgpack', body: body.to_msgpack ) end # 開発時のみJSONを許可する事もできる format.json do render json: body.to_json end if Rails.development? endend
37
Client側の実装例
private void SendSampleCreateRequest(string name){ SampleCreateRequest request = new SampleCreateRequest(); request.name = name; // URL, メソッドなどがラップされた、自動生成されるクラス SampleCreate api = new SampleCreate(request); // コールバックの登録 api.onComplete = OnSampleCreateComplete; api.onError = OnSampleCreateError; WebRequester.Request(api)}
38
型の違いの吸収
コードジェネレータが生成するファイルで吸収する
type(:sample) { param :id, :int64}
class Type::Sample attribute :id, Integer # ruby側はIntegerとして扱うend
class Sample { public long id; // C#側はlongとして扱う}
struct Sample { int64_t id; // C++側はint64として扱う}
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
We are hiring!
Aimingではゲーム開発に興味のある、
ゲームの好きなWebエンジニアを募集しています!
https://aiming‑inc.com/ja/jobs/
49