【サポーターズCoLab勉強会】スマホゲームサーバのしくみをしってみよう メモ

# [【サポーターズCoLab勉強会】スマホゲームサーバのしくみをしってみよう](https://supporterzcolab.com/event/575/) メモ

## ガイダンス

りっくん さん

サポーターズでは、ほぼ毎日勉強会・講演会を開催している

* オフィス無料貸出
* 講師として登壇
* 転職とか企業の相談

### 宣伝

* サポーターズ広報サトアズ（佐藤アズサ）さん [@satoazu_sp](https://twitter.com/satoazu_sp?lang=ja)
* Twitterハッシュタグ [#spzcolab](https://twitter.com/hashtag/spzcolab?src=hash)
* サポーターズ公式LINE@リリース
* 勉強会に参加すると抽選でCOMPが当たる

### 学生向けキャリア支援サービス地方の就活を変える
地方からの交通費を支給


### 勉強会の目的

* スキルアップ
* インプット・アウトプット → 新たななにかを生み出す

# [スマホゲームサーバーのしくみをしってみよう](https://speakerdeck.com/kajitack/sumahokemusahafalsesikumiwositutemiyou)
梶川琢馬 さん

サーバエンジニア3年目

## 過去の発表

* Mysqlの基礎
* [DBテーブルの設計入門](https://speakerdeck.com/kajitack/dbteburushe-ji-ru-men)
* [オンラインゲームのしくみを知ってみよう](https://speakerdeck.com/kajitack/onraingemufalseshi-zu-miwositutemiyou)


## スマホゲームの特徴
売上が大きいゲームの特徴

### スマートフォンゲームのゲームサイクル
* キャラ育成 → バトル・クエスト → キャラ育成
* シングル＋マルチプレイモード
* マルチプレイではよりリアルタイム性が求められる

### 課金モデル
* 無料＋アプリ内課金
* ガチャ・スタミナ回復・時短など
* 高可用性が求められる
  * ガチャが引きたいときに引けない
  * せっかく時短したのにサーバ障害

### スマホゲームサーバの特徴
* 大量のアクセス
* データの更新頻度が高い
* ピーク時と平常時に求められる性能の差が大きい
* メンテナンスをなるべくしない
* 頻繁にコンテンツや機能の追加

### データの特徴
* ユーザーデータ
  * ユーザのレベルや所持アイテム所持金など
* マスタデータ
  * ゲームのステージやキャラなどの運営が管理するデータ
* ログデータ
  * KPI分析や障害調査


## システムの構成例と通信の流れ

### ゲームモードで変わるシステム構成

#### アウトゲーム
* キャラ育成・ガチャ・バトル開始・終了など
* HTTP(S)通信
* Webアプリの構成に近い

<pre>
            CDN ― Assetサーバ
         ／               DBサーバ
      ／               ／
スマホ ― LB ― APIサーバ ― KVSサーバ
                       ＼
                          Logサーバ
</pre>

#### APIサーバ
* リクエスト先はロードバランサによって決まる
* 一般的なWebアプリのAPIと同じ構成
* Apache/Nginx
* PHP/Java/Goなど
* スケールアウト・スケールインを柔軟に
  * ピーク時と平常時に求められる性能の差が大きい
* 機能ごとにサーバを分割することもある
  * 課金・認証・マッチング
  * 特定の機能に特価することで開発に専念

#### データストア
* DB（mySQL、PostgreSQLなど）
  * 永続化データ
* KVS（Redis、Memcachedなど）
  * キャッシュ、アクセス頻度が高く消えても問題ない
* ログ（BigQueryなど）
  * 分析用のデータ

> ユーザーデータ、ゲームのマスターデータはMySQL、
> DBキャッシュはMemcached、ランキングなど永続化したいデータはRedisと使い分けることが多い
> ログはサーバからストレージに溜め、BigQueryに投入する


#### アセットサーバ（DLサーバ）
* 静的ファイルの配信用サーバ
  * 画像や音楽・3Dモデルなど
* CDNでキャッシュさせる
  * 新バージョンだったらクライアントがリクエストする

#### アセット取得の流れ
1. アセットの種別ごとにバージョンを管理するDBを用意
1. 新しいアセットをデプロイしたらDBのバージョンを上げる
1. クライアントがアセットのバージョンを確認するリクエストをする
1. サーバはDBのバージョンを返す
1. クライアントで保持しているバージョンと違うアセットがあったらそのアセットをダウンロード

#### アウトゲームの処理の流れ
* 画面遷移時・データ更新時にサーバにリクエスト
* サーバがDBやKVSのデータを取得・更新
* 一度のリクエストでデータの追加や更新・取得
  * リクエストメソッドの使い分けはほぼGETかPOST
  * RESTful APIはNG（一回のリクエストで大量のデータを取得するため）



### インゲーム（マルチプレイ クライアント/サーバ方式）
バトル中の味方や敵の位置・攻撃・チャットなど
WebSocketやP2Pなどの双方向通信

<pre>
スマホ                  DBサーバ
      ＼             ／
         バトルサーバ ― KVSサーバ
      ／             ＼
スマホ                  ログサーバ
</pre>

#### バトルサーバ
* 接続先はバトル開始時にAPIサーバで振り分けられる
* リアルタイム性が求められる
  * WebSocketやリアルタイムゲームエンジン
  * C++、Node.js、Goなど
* サーバ集中型とクライアント分散型
* ゲームによって特徴がある

#### サーバ集中型
* サーバ側でゲームロジックや進行を行う
* バトル中サーバ側で必要なデータを保持
* 端末間でズレるとまずい処理
  * 対戦ゲームでのキャラの位置や当たり判定
  * 協力バトルのボスの位置やHP（ザコ敵はそこまで重要ではないためクライアントで処理）
* サーバの判定処理を待つ必要がある（ラグが発生しやすい）

#### クライアント分散型
* それぞれの端末でゲームの進行を行う
* ゲームの進行上、端末間のズレが気にならない場合
  * 協力バトルのザコ敵
* 不正対策はしづらい
* 擬似的なP2P、パケットのブロードキャストに専念する

> 完全にP2Pで実装するとサーバの負荷は無くなるが、ホストプレイヤーが切断するとゲームが終了してしまう

#### インゲームの処理
パーティを組んで戦うアクションバトルの例

1. パーティ作成or参加
1. ルーム（バトル空間）を作成、バトル開始
1. ルーム内でバトル
1. バトル終了、ルーム削除、バーティ解散

ルーム入場からバトル開始まで

1. Client（以下CL）→APIサーバ（以下API）：パーティ参加リクエスト
1. API→DB：パーティがあるか確認、なければ作成
1. API→CL：バトルサーバの接続先を返す
1. CL→バトルサーバ（以下バトル）：ルーム作成リクエスト
1. CL→API：バトル開始

バトル中

1. CL→バトル：コマンドを送信
1. バトル→CL：バトル情報を返す
1. バトル→CL：イベント発生を通知

バトル終了

1. CL→バトル：バトル終了をリクエスト
1. バトル→CL：ルームを削除、切断
1. CL→API：リザルトのリクエスト
1. CL→API：パーティを解散



### その他、運営に必要なシステム
* レポジトリサーバ、デプロイサーバ
  * ソースコードやアセットの管理、デプロイに使い
* 運営やカスタマーサポート用の管理ツール（Webサービスのダッシュボード）
  * データの確認、お知らせやバナーの反映、補填など
* KPI分析
  * ログデータの分析や可視化、社内共通


## ゲームの開発・運用で気をつけるべきこと
### DBの負荷を考慮しよう
* DBが負荷のボトルネックになりがち
* オンラインでスキーマの変更がし辛い
* インデックスやDBのパーティショニング
* 集計データはバッチ処理してキャッシュさせる
* なるべく少ないクエリ数でデータを取得

### DBの負荷分散
* 水平分割
  * ユーザーのIDごとにDBサーバを振り分ける
  * 期間限定のDBを日付でパーティショニング
* Master/Slave構成
* マネージドサービスを使うのもあり

> ユーザーDBは書き込みが多くボトルネックになりやすい
> DB1台でどのぐらいのユーザーを捌けるかを検証すること
> アップデートでテーブルが追加されるので、スペックに余剰をもたせること
> ユーザーIDでDBサーバを分割する場合他のユーザーデータへのアクセス時のクエリが増加するため、
> アクセス頻度を減らす工夫が必要

### オンラインメンテ
* DBのデータ設計を変えたときや定期メンテをオンラインでやりたい
* Master DBがネック
  * Master/Slave切り替え
  * Percona Tool Kit Online Schema Change(MySQL)
* マネージドサービスを使うのもあり

### リリース後のアラート監視
　* サーバが止まる前に対処する
　* 各種サーバのエラー、CPU使用率、リクエスト数
　* DBサーバのクエリ数
　* レスポンスタイム
　* Disc write、使用量
　* **お客様の問い合わせ、Twitterでエゴサーチ**

### ゲーム運用で便利なツールを作る
* データ管理の仕組みを作ろう
  * ExcelやスプレッドシートからSQLへの変換
* デバッグ機能や管理ツールを作ろう
  * 開発スピードUP
  * 管理ツールの権限管理は重要

## まとめ
* ゲームシステムの構成はWebアプリと一緒
* アクセス数や更新頻度が高くなる傾向
* リアルタイム性の求められるシステム講師絵はゲームによって異なる
* 高い可用性を保つためにオンラインメンテ・アラート監視などの工夫が必要

## 質疑応答
### 可用性について、サーバのエリア分割などをしていますか？
GCPで複数のリージョンを使い分けている

### 北海道の地震は影響あったか？
そのときは無かった
北海道の方が遊べない時間が発生したのでイベント期間を延長するなどを検討した

### リアルタイム通信について、パケットロスの対策はしているか？
シーケンス番号をパケットに付与し、不足していればパケロスを検知できるようにしている

### ゲームのサービスが始まったときにアクセスが多いと障害が発生していることがある。　なぜ発生するのか？
サーバ見積もりミス（想定よりユーザーが多い）
プログラムの実装により想定以上の負荷が発生
データベースへのアクセスが多すぎて、APIサーバを増やすだけでは対応できず障害化する
事前にDBをパーティション分け、分割しておく（できるようにしておく）

### お客様からの声、Twitterエゴサなど、それらからどのように改善していくのか？
不具合・エラーの対応については、事実確認 → お知らせ で告知
キャラクターが強すぎてゲームバランスが悪いなど、プランナー案件
機能追加は、運営の方針と合っていれば追加されることもある