Slide 1

Slide 1 text

Goでゲームサーバー実装をして 考えたこと Goを書いて楽しかった思い出

Slide 2

Slide 2 text

目次 ● 自己紹介 ● Go ● Goとカヤック ● Goで開発したサーバーの説明 ● Goの実装で選択したこと ● まとめ

Slide 3

Slide 3 text

自己紹介 ● 名前 ○ 水野 敬太 (@mizkei11) ● 所属 ○ 面白法人カヤック ● 職種 ○ サーバーサイドエンジニア

Slide 4

Slide 4 text

Go

Slide 5

Slide 5 text

Go Programming Language 下記のような特徴を持つプログラミング言語 ● 静的型付け ● コンパイルされる ● インタプリタ言語のような手軽さ ● 並行処理を単純に扱うための仕組み ● 意識することなくマルチコアを使い切る

Slide 6

Slide 6 text

CLIツール開発におけるGo ● ツールの配布が容易 ○ クロスコンパイルが簡単 ○ コンパイル早い ● ふわっと書いてもそれなりに高速に動作する

Slide 7

Slide 7 text

Web開発におけるGo ● 標準パッケージで簡単にサーバーが書ける ● スレッドやプロセスなど考えなくて良い ○ Goが勝手にCPUを使い切ってくれる

Slide 8

Slide 8 text

チーム開発におけるGo ● 不要な作業の削減 ○ gofmt, golintによって一つの書き方に収束する ● シンプルな文法 ○ 関数を辿っていけば、大体全部わかる ■ 魔法のような動作はない ■ GoはGoで書かれているので標準パッケージの中までわかる ○ ただし、コード量は多くなる

Slide 9

Slide 9 text

Goとカヤック

Slide 10

Slide 10 text

Go言語を推進していく宣言 ● 「カヤックは、Go言語を積極的に推進していきます」 ○ https://www.kayac.com/news/2014/07/golang 社内ツールやインフラ関連ツールは積極的にGoで書かれている (※無理に選択しているという意味ではない)

Slide 11

Slide 11 text

勉強会 ● kamakura.go(in 横浜) ○ https://connpass.com/event/87417/

Slide 12

Slide 12 text

Goで実装したゲー ムサーバーの説明

Slide 13

Slide 13 text

ゲームシステム概要 ● PvP ○ 対戦自体の通信は外部のサーバーを利用 ● プレイヤーが集まって形成するグループ ○ チャット、対戦の共有 ● 対戦結果によるランキング ○ 上位者の報酬配布

Slide 14

Slide 14 text

API ● HTTPのGET/POSTを受け取り、JSONを返す

Slide 15

Slide 15 text

定期実行処理 ● イベントとして発行されるタスクを複数台のサーバーで処理 ○ 通常のリクエストから発生するバックグラウンドタスクも 同じ流れで処理される

Slide 16

Slide 16 text

管理画面 ● HTMLのページ ● ユーザー情報の管理 ○ 行動履歴、アカウントの引き継ぎ、個別メッセージの送信、 課金履歴など ● 運営からのお知らせ管理

Slide 17

Slide 17 text

マッチング ● スコアの近い、待ち時間の長いユーザー ● バックグラウンドタスクとして実行して、 マッチング完了でクライアント側にマッチング情報を通信

Slide 18

Slide 18 text

Goの実装で選択 したこと

Slide 19

Slide 19 text

Goにおける色々な選択 ● カヤックのゲーム開発ですべてGoによる実装は初めて ○ packageや構成についていろいろ考えなければならない

Slide 20

Slide 20 text

Web Application Framework ● GoのWAF ○ echo (https://github.com/labstack/echo) ○ Gin (https://github.com/gin-gonic/gin) ○ Goji (https://github.com/zenazn/goji) ○ ... 他にもたくさん

Slide 21

Slide 21 text

WAFは使わなかった ● net/http ○ 標準パッケージ ○ だいたいこれで解決できる ○ WAFを使わないからと記述量が多くなるわけではない ○ ミドルウェアも普通に書けば良い ■ func(http.Handler) http.Handlerとか用意するだけ

Slide 22

Slide 22 text

ハンドラー周辺で使ったpackage ● https://github.com/gorilla/schema ○ リクエストのパラメータをパースしてstructに詰め込む ● https://github.com/julienschmidt/httprouter ○ 管理画面はhtmlで遷移していくため、パスのパラメータを利用したい

Slide 23

Slide 23 text

ハンドラーの自動生成 ● Baal ○ 「IDL「Baal」について」 ■ http://techblog.kayac.com/unity_advent_calendar_2016_20 ○ クライアントと通信するパス/リクエスト/レスポンスの内容を定義 ■ 型や文字列の長さ、リストの長さなども記述可能

Slide 24

Slide 24 text

● ほとんど自動生成 ○ パスパターン ○ リクエスト/レスポンスのstruct ○ リクエストの内容をstructに詰め込む ○ 値の範囲、文字列の長さ、リストの長さのバリデーション ● 手で書かないといけない処理 ○ リクエストのstructの値をモデルのメソッドに値を渡す ○ モデルのメソッドの返り値をレスポンスのstructに詰め込む ハンドラーの自動生成

Slide 25

Slide 25 text

自動生成した副作用 ● リクエスト/レスポンスのstructのdecode/encodeがある ○ apiのテストコードを書く際にテストケース作成が楽 ○ デバッグ用のシナリオをGoで書くのが楽 ○ 負荷試験時のシナリオ作成が楽

Slide 26

Slide 26 text

ORM ● GoのORM ○ https://github.com/jinzhu/gorm ○ https://github.com/go-gorp/gorp ○ https://github.com/go-xorm/xorm ○ … やっぱりたくさんある

Slide 27

Slide 27 text

ORM使わなかった ● 実行されるクエリがどうなるのかわからない ● ライブラリである以上、interface{}を扱うしかない場合がある ● ORMの気持ちを考えるのは非常に難しい

Slide 28

Slide 28 text

DB処理周りで利用したパッケージ ● https://github.com/Masterminds/squirrel ○ クエリビルダー ○ sqlをそのまま書くよりはGoの中の型として扱える部分が増える ■ でも、そんなに変わらないので普通に sqlも書いている

Slide 29

Slide 29 text

DBへのクエリ処理の自動生成 ● sqlファイルからrowのstruct、特定のクエリの自動生成 ○ https://github.com/schemalex/schemalex でsqlをパース ● マスターデータはメモリ上にキャッシュ ○ 自動生成されるselectなどの処理の裏でメモリから取ってくる

Slide 30

Slide 30 text

DBへのクエリ処理の自動生成 ● Insert/Update/Refetch ○ 単純な操作達 ● RefetchForUpdate ○ 自動生成でrowにIsForUpdate() boolを作っている ● ScanRows/ScanRow ○ rowに値詰め込む処理は自動

Slide 31

Slide 31 text

DBへのクエリ処理の自動生成 ● Primary Key、Uniqueなものはメソッドを用意 ○ 例)userテーブル: Primary Key id 、Unique (name, number) ■ func (t User) FindByID(id int64) (*row.User, error) ■ func (t User) FindByNameNumber(name string, number int64) (*row.User, error)

Slide 32

Slide 32 text

DBへのクエリ処理の自動生成 ● eachRowsFast ○ 条件を満たす全rowを順に処理していくためのメソッド ○ func([]*row.User) errorみたいなメソッドを渡して、 大量のrowを使った処理などをおこなう ● selectWithCursorID ○ 主に管理画面用にカーソルを使ってページングするためのメソッド ○ 前後ページのIDを返したり、並び替えをおこなう

Slide 33

Slide 33 text

DBへのクエリ処理の自動生成 ● マスターデータのキャッシュ ○ Primary Key、Uniqueに対してGoのmapを生成 ○ FindByIDとかの裏側でmapから取得

Slide 34

Slide 34 text

DBへのクエリ処理の自動生成 ● 自動のScanは全部の値を取ってきてしまう ○ 通信のサイズ、メモリなどの無駄 ○ 仕方ないので、もう手で書く ■ 余分な取得が2,3カラム程度ならもう全部取ってしまうので、多くはない

Slide 35

Slide 35 text

ファイルをバイナリに詰め込むpackage ● ファイル1個にまとまるので配置が楽になる ○ https://github.com/jessevdk/go-assets ○ https://github.com/jteeuwen/go-bindata ■ これは消えたリポジトリから forkされたリポジトリ

Slide 36

Slide 36 text

詰め込むpackage使いませんでした ● ECSでサービスを動かしているため、dockerのimageの中に ファイル配置すれば良い ○ そんなに1個のファイルになっても嬉しくなさそう ● 開発の途中でgo-bindataのリポジトリが消えた

Slide 37

Slide 37 text

チームでの開発 ● サーバーサイドは5人で開発 ● レビュー ○ コードは他の4人すべてのレビューを通らないとマージされない

Slide 38

Slide 38 text

コードの書きやすさ ● 特殊な書き方ができない ○ コードとしての書き方にほとんど迷わない

Slide 39

Slide 39 text

コードの読みやすさ ● シンプルな文法 ○ 読みにくいコードはほぼ書けない ● gofmt/golint/govet ○ ロジックにかかわらないところは何も考えなくて良い レビューする人が多くても開発の速度をあまり下げない

Slide 40

Slide 40 text

楽しかったこと(個人の感想) ● Go(とアプリ)のことだけ考えていることができる ○ フレームワークの{waf}.Contextや使い方やDocsは気にしたくない ○ ORMの気持ちも考えていたくない ■ いつ消えるかもいつ気持ちが変わるかもわからない

Slide 41

Slide 41 text

まとめ ● Goは楽しい ● 標準パッケージで大体問題ない ○ もちろん便利なパッケージは使う ○ バージョンを気にしたり、そのパッケージの特殊な書き方を 覚えないといけない大きなフレームワークには依存しなくてもよい ● チームが大きくても開発の速度が落ちにくい