Pro Yearly is on sale from $80 to $50! »

Goでゲームサーバーを実装して考えたこと / game server in go

03518666631462b711589a1b8c4048a2?s=47 mizkei
May 14, 2018

Goでゲームサーバーを実装して考えたこと / game server in go

IGDA日本 ゲームサーバ勉強会 #8
https://techplay.jp/event/666753

登壇した資料のようなメモ

03518666631462b711589a1b8c4048a2?s=128

mizkei

May 14, 2018
Tweet

Transcript

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

  2. 目次 • 自己紹介 • Go • Goとカヤック • Goで開発したサーバーの説明 •

    Goの実装で選択したこと • まとめ
  3. 自己紹介 • 名前 ◦ 水野 敬太 (@mizkei11) • 所属 ◦

    面白法人カヤック • 職種 ◦ サーバーサイドエンジニア
  4. Go

  5. Go Programming Language 下記のような特徴を持つプログラミング言語 • 静的型付け • コンパイルされる • インタプリタ言語のような手軽さ

    • 並行処理を単純に扱うための仕組み • 意識することなくマルチコアを使い切る
  6. CLIツール開発におけるGo • ツールの配布が容易 ◦ クロスコンパイルが簡単 ◦ コンパイル早い • ふわっと書いてもそれなりに高速に動作する

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

  8. チーム開発におけるGo • 不要な作業の削減 ◦ gofmt, golintによって一つの書き方に収束する • シンプルな文法 ◦ 関数を辿っていけば、大体全部わかる

    ▪ 魔法のような動作はない ▪ GoはGoで書かれているので標準パッケージの中までわかる ◦ ただし、コード量は多くなる
  9. Goとカヤック

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

  11. 勉強会 • kamakura.go(in 横浜) ◦ https://connpass.com/event/87417/

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

  13. ゲームシステム概要 • PvP ◦ 対戦自体の通信は外部のサーバーを利用 • プレイヤーが集まって形成するグループ ◦ チャット、対戦の共有 •

    対戦結果によるランキング ◦ 上位者の報酬配布
  14. API • HTTPのGET/POSTを受け取り、JSONを返す

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

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

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

  18. Goの実装で選択 したこと

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

  20. Web Application Framework • GoのWAF ◦ echo (https://github.com/labstack/echo) ◦ Gin

    (https://github.com/gin-gonic/gin) ◦ Goji (https://github.com/zenazn/goji) ◦ ... 他にもたくさん
  21. WAFは使わなかった • net/http ◦ 標準パッケージ ◦ だいたいこれで解決できる ◦ WAFを使わないからと記述量が多くなるわけではない ◦

    ミドルウェアも普通に書けば良い ▪ func(http.Handler) http.Handlerとか用意するだけ
  22. ハンドラー周辺で使ったpackage • https://github.com/gorilla/schema ◦ リクエストのパラメータをパースしてstructに詰め込む • https://github.com/julienschmidt/httprouter ◦ 管理画面はhtmlで遷移していくため、パスのパラメータを利用したい

  23. ハンドラーの自動生成 • Baal ◦ 「IDL「Baal」について」 ▪ http://techblog.kayac.com/unity_advent_calendar_2016_20 ◦ クライアントと通信するパス/リクエスト/レスポンスの内容を定義 ▪

    型や文字列の長さ、リストの長さなども記述可能
  24. • ほとんど自動生成 ◦ パスパターン ◦ リクエスト/レスポンスのstruct ◦ リクエストの内容をstructに詰め込む ◦ 値の範囲、文字列の長さ、リストの長さのバリデーション

    • 手で書かないといけない処理 ◦ リクエストのstructの値をモデルのメソッドに値を渡す ◦ モデルのメソッドの返り値をレスポンスのstructに詰め込む ハンドラーの自動生成
  25. 自動生成した副作用 • リクエスト/レスポンスのstructのdecode/encodeがある ◦ apiのテストコードを書く際にテストケース作成が楽 ◦ デバッグ用のシナリオをGoで書くのが楽 ◦ 負荷試験時のシナリオ作成が楽

  26. ORM • GoのORM ◦ https://github.com/jinzhu/gorm ◦ https://github.com/go-gorp/gorp ◦ https://github.com/go-xorm/xorm ◦

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

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

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

  30. DBへのクエリ処理の自動生成 • Insert/Update/Refetch ◦ 単純な操作達 • RefetchForUpdate ◦ 自動生成でrowにIsForUpdate() boolを作っている

    • ScanRows/ScanRow ◦ rowに値詰め込む処理は自動
  31. 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)
  32. DBへのクエリ処理の自動生成 • eachRowsFast ◦ 条件を満たす全rowを順に処理していくためのメソッド ◦ func([]*row.User) errorみたいなメソッドを渡して、 大量のrowを使った処理などをおこなう •

    selectWithCursorID ◦ 主に管理画面用にカーソルを使ってページングするためのメソッド ◦ 前後ページのIDを返したり、並び替えをおこなう
  33. DBへのクエリ処理の自動生成 • マスターデータのキャッシュ ◦ Primary Key、Uniqueに対してGoのmapを生成 ◦ FindByIDとかの裏側でmapから取得

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

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

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

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

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

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

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

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

    • チームが大きくても開発の速度が落ちにくい