rails api way in aiming

51
Rails‑API Way in Aiming 1

Upload: yasutomo-uemori

Post on 16-Apr-2017

89 views

Category:

Software


1 download

TRANSCRIPT

Rails‑API Way in Aiming

1

自己紹介

名前:植森 康友役職:ソフトウェアエンジニア

経歴:2年目

担当:

WebAPI開発

管理ツール開発

インフラ?(ミドルウェア~)

ansible

Docker

td‑agent

bigquery

2

今回お話すること

Aimingについて

Aimingの開発スタイル

MMORPGのWebAPI開発について

AimingでのWebAPI on Rails

3

Aimingについて

企画、開発~運営までを行うゲーム開発会社

オンラインゲームの開発を得意としている

各種人材募集中です!

4

代表作:剣と魔法のログレス いにしえの女神

800万ダウンロード達成

スマホで簡単MMORPG

5

Aimingの開発スタイル

6

採用技術の選定: 各プロジェクトが適切だと思ったものを選ぶ

プロジェクトメンバーの技術スタックがさまざま

C#, C++, Ruby/Rails, PHP...

外部委託をする/しない

開発するゲームの方向性

アクションがメインなのかRPGがメインなのか

リアルタイムに通信をするのがどこからどこまでなのか

プロジェクトごとの事情

納期

7

採用されやすい技術/言語

クライアント

C#(Unity): 新規開発はほぼUnity

C++(cocos‑2dx)、ブラウザ: 剣と魔法のログレスなど

リアルタイム

C++:昔からPCゲームなどを作ってきたエンジニアが多数在

籍している

C#:クライアントと処理を共通化しやすいため、一部プロジ

ェクトが採用

WebAPIRuby(Rails): 新規開発の多くはほぼRails

Python(Flask): 剣と魔法のログレスなど

8

その他

Websocket: チャットなどで一部のプロジェクトが採用NodeJS

Ruby

9

まとめ

かなりカオスな多様性のある開発をしてきた

新規開発は大まかな構成が固まりつつある

クライアント:Unity

リアルタイム

大阪:C++

東京:C#

WebAPI:Rails

10

MMORPGのWebAPI開発について

11

大なり小なりリアルタイム性が必要

大人数がそのリアルタイムを共有する

他者とのコミュニケーションがある

MMORPGの特徴

※注)剣と魔法のログレスではRailsは使っていません

12

Q. この画面を表示するのにどういうAPIが必要でしょうか(10秒)

13

リアルタイムサーバの役割はどこま

で?

チャットは何で通信してる?

クライアントはどのデータをどこまで

キャッシュしてる?

見た目情報はクライアントが直接

WebAPIから取得する? リアルタイムサーバが教える?

考える事多すぎ\(^o^)/

A. 要件による

14

リアルタイムサーバ

エリアチャット

キャラクタの位置と見た目

チャンネル情報

WebAPIサーバ

自分のレベル、ジョブ、所有ポイン

ト、見た目情報などはログイン情報と

してWebAPIから

剣と魔法のログレスの場合

15

この画面で欲しい情報

キャラクターのステータス

キャラクターの装備アイテム

キャラクターの見た目情報

各種装備のレベル

アイテム所持数

考えたいこと

全武器の情報はこの時点で必要?

他のタブの情報はこの時点で必要?

装備が変更されたらいつ送る?

ステータスはどっちで計算する?

例)装備画面を表示する場合

16

MMORPGのWebAPI開発のポイント

リアルタイム/WebAPIサーバ/クライアントそれぞれがどこまで

やるのか線引をする

一つの責務はなるべくどちらか片方のサーバだけが持つ

なるべく通信頻度/通信量を減らす

できるだけ一度の通信で取ってこれるようにAPIを設計する

その通信でどこまで取ってくるかはクライアントと相談

「使う側が使いやすいAPI」を設計する

一番工数がかかるのはクライアント側

WebAPI側の仕様に合わせてクライアント側が頑張らなくて良いようにする

17

AimingでのWebAPI on Rails

18

AimingにおけるWebAPIの実装スタイル

RPCスタイルREST APIではなく、RPCスタイルを採用

必要な情報群と1�1になるエンドポイントを作る

コードジェネレータの利用

メッセージの型と形式を定義するファイル

定義ファイルから必要なファイルを自動生成する

19

RPCスタイル

20

RPC?

Remote Procedure Call

プログラムからネットワークを通じて手続きを呼び出す

実行する手続きと引数を要求メッセージとしてサーバに送信する

21

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

コードジェネレータ

25

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側の実装例

35

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開発でRailsを使うこと

40

迫りくるマイクロサービスの波

以前:いくつかのプロジェクトでリアルタイムサーバが巨大化しす

ぎていた

リアルタイムサーバがほとんどなんでもやる

WebAPIは必要最小限のサブシステム

現在:各々が得意なことを役割分担する方向へ自然と移行

パフォーマンスが必要なものはリアルタイムサーバ

パフォーマンスが必要ないものはWebAPIサーバ

41

Pros: 実装の速さ

実装だけで見れば、簡単なAPIならテスト込みでも1~2時間程度で実装できる

難しいところも、実装よりも要件定義などの方が時間がかかること

がほとんど

42

Pros: エコシステムが充実している

Rack middleware, Gemなどのエコシステム難しい実装でも既存gemを使う、あるいは参考にして実行すれ

ば実装が楽に終わる

ex) DBの水平分割など

Rails自体のエコシステム長く使われてきたフレームワークだけあって、rails自体にはほとんど手を入れる必要なくやりたい実装が出来る

「こんな機能あったのか」となることも少なくない

43

Pros: アーキテクチャが枯れている

実装面のベストプラクティスが豊富

fat controller, fat modelなどの問題を経て設計パターンが成熟

している

社内に経験豊富なエンジニアがいるというのも強み

44

Pros: 管理ツールの実装が楽

ゲーム開発では運営・管理を行うためのツールが必須

例)ギフトの配布、ユーザサポート、KPI分析など

APIサーバがRailsで作られているため、管理ツール側でmodelやモジュールなどを流用しやすい

45

Cons: パフォーマンス

やはりパフォーマンスは静的言語に比べると劣る

最近はGolangやPhoenixなどの台頭もある

46

Cons: Railsエンジニアの数が少ない

正確には「ゲーム業界に来るRailsエンジニア」の数が少ない

管理ツールなどのWebアプリケーションから共通基盤、WebAPIと幅広い領域をカバーするだけの人が足りていないのが現状

47

そこで、

48

We are hiring!

Aimingではゲーム開発に興味のある、

ゲームの好きなWebエンジニアを募集しています!

https://aiming‑inc.com/ja/jobs/

49

ご静聴ありがとうございました

50

質疑応答

51