2016/04/20
株式会社グレンジ
塚原 裕也
ポコロンダンジョンズと
リアルタイム通信
-サーバサイド編-
‣名前
塚原 裕也
‣所属
株式会社グレンジ
‣経歴
・2008/04 SIのベンチャー企業に入社
・2012/02 Cyber X入社
- アイドルレボリューション
- メジャーリーグオールスターズ
- NBA2Kオールスターズ
- サカつくSワールドスターズ
・2014/11 グレンジへ異動
- ポコロンダンジョンズ
自己紹介
アジェンダ
‣ポコダン概要
‣利用技術
‣サーバ構成
‣データ
‣機能
‣発生した問題と対応
ポコダン概要
ポコダン概要
‣リリース
iOS:2014年6月
Android:2014年8月
‣最新バージョン
3.6.1
‣ダウンロード数
500万
‣マルチプレイ導入
2015年3月
開発期間は8ヶ月位
当初は2014年内に導入予定だった
‣チーム人数
45人弱
サーバーサイド:3.5人
クライアントサイド:6.5人
ポコダン概要
•Nginx(1.4.4)
•PHP(5.5.7)
•Phalcon(1.2.6)
•MariaDB(5.5.38)
•Redis(2.8.4)
•Memcached(1.4.15)
利用技術-非リアルタイム通信部分-
• Nginx(1.4.4)
• Node.js(v0.10.32)
• Socket.IO(0.9.17)
クライアントのライブラリに合わせて0.9.17を選択。
接続時にハンドシェイクが遅延した場合に一定期間
ハンドシェイクを待つパッチを当てて利用している
。
https://github.com/tico8/socket.io/tree/0.9.16-patched
• Redis(2.8.4)
利用技術-リアルタイム通信部分-
‣ Socket.IOとは
• Websocket、xhr-polingをサポート
クライアントの環境次第で自動的に切り替え
• Websocket通信を数行で始められる
• 接続確立は2段階方式
1.ハンドシェイク
2.接続要求
3.接続確立
利用技術-リアルタイム通信部分-
‣ RedisのPub/Subとは
• publish(発行)、subscribe(購読)の略
• イベントが発行されると、購読者へ通知される
‣ マルチプレイでの利用場面
• データ更新通知
• プレイ情報の同期
利用技術-リアルタイム通信部分-
・・・
ロードバランサ
・・・
データ用pub/sub用
ロードバランサ
websocket https
サーバ構成-全体-
• BIG-IP製
• SSL対応
• ラウンドロビン+IP persistence設定
- 同じIPアドレスは、同じnodeサーバへ
- persistenceのキャッシュ削除間隔を1秒程度
- マスクは255.255.255.255
最近まで255.0.0.0になってて偏りが・・・
• sticky sessionを利用
Socket.IOのコネクション確立通信(2発目)を同サー
バに振るため
サーバ構成-ロードバランサ-
ロードバラン
サ
②
①
同じIPは同じサーバへ振れるが、Socket.IOで問題が・・・
‣ persistenceありの場合
サーバ構成-ロードバランサ-
‣ サブネットマスク
• 255.0.0.0の場合
第1オクテットのみで振り分け
192.0.0.0~192.255.255.255
IPアドレス数:16777216
→均等に割り振られない
• 255.255.255.255の場合
第4オクテットまで振り分け
IPアドレス数:1
→均等に割り振られる
サーバ構成-ロードバランサ-
ロードバラン
サ
‣ sticky sessionなしの場合
①handshake
②connect
handshakeとconnectで違うサーバーへ振られてしまう可能性が
(MemoryStore利用時)
サーバ構成-ロードバランサ-
ロードバラン
サ
①handshake
②connect
③connected
handshakeとconnectが同じサーバーへ振られるため、コ
ネクションが確立できる。
(MemoryStore利用時)
‣ sticky sessionありの場合
サーバ構成-ロードバランサ-
‣ Nginx
• 80番ポートで受け
• 3000番ポートでNode.jsに流し込む
• リバースプロキシでサブドメインを割り当てた
Node.jsに振る
‣ Node.js
• 1マスタープロセス、1ワーカプロセス
※ワーカプロセスがCluster構成にできなかった
• foreverを利用しプロセス永続化
サーバ構成-Webサーバ-
server {
listen 80;
server_name localhost;
index index.php index.html index.htm;
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
underscores_in_headers on;
location ^~ /pocolon_dungeons_node/ {
proxy_pass http://127.0.0.1:3000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location ^~ /pocolon_dungeons_node/socket.io/ {
…
}
/etc/nginx/conf.d/pocolon_dungeons_node.conf
サーバ構成-Webサーバ-
• pub/sub用とデータ用の2種類
• pub/sub用はルーム単位で振り分け
• データ用はPHP側からも利用する
サーバ構成-Redisサーバ-
データ-Redisのキー-
1.マッチング検索用(ソート済みセット型)
位置情報、ルームIDを登録
2.ルーム情報用(ハッシュ型)
ルーム詳細情報を登録
・ダンジョン情報
・ステータス管理(マッチング中、クエスト中)
・位置情報
・アプリバージョン
3.メンバー情報用(ハッシュ型)
デッキ情報(装備/モンスター)などを登録
4.ロック用(文字列型)
ルーム情報、メンバー情報のキーを更新
する際のロックに利用。
SETNX()+EXPIRE
SETNX=“SET if Not eXists”
キーが存在したらセット出来ないのを利
用し、キーのセットと削除でロックを実
現。
データ-Redisのキー-
機能-処理の流れ-
• ルーム作成/参加
PHP⇒Node.jsの一連の処理が完了して、Websocketのコネク
ションが確立出来たら処理完了。
• クエスト開始
PHP側でDBに登録後、Node.jsで待ち合わせ処理をし
、マルチプレイ開始。
• クエスト中
基本的にWebsocket通信だが、DB操作が必要な場面は
PHP側で処理される。(コンティニュー等)
• クエスト終了
Websocketのコネクション破棄後、PHP側でDB更新
が行われる。
•マルチプレイ以外の機能全て
•ルーム作成、参加
※websocketのコネクション確立部分はNode.js
•ルーム検索
•その他(シングルプレイ共通部分)
プレイヤー情報取得、ドロップ抽選、
ログなどDB操作が絡む処理
•RedisのデータはNode.js側と共有
機能-非リアルタイム通信部分-
機能-リアルタイム通信部分-
•クエスト開始後のほぼ全ての処理
コンティニューなどDB操作をする際はPHPの通信も行っている
•コネクション確立、破棄
コネクション時のユニークIDをRedisに登録している
•クライアントから送られたデータを受け流す
ゲームロジックは持たない。
•同期
フロア待ち合わせ、盤面復旧。
•ソケット切断
一定時間応答のないプレイヤーを切断。
•ソケット再接続
‣PHP側
•位置情報、合言葉などを受け取り
•Redisデータ登録
マッチング検索用、メンバー情報用などキーを
複数に分けている。
•デッキ情報など表示に使うデータを返す
‣Node.js側
•ユーザID、ルームIDなどを受け取り
•Redisデータ更新
ルーム情報のステータスをマッチング中に。
ソケット接続完了ステータスに。
•デッキ情報など表示に使うデータを通知
機能-ルーム作成-
‣PHP側
•位置情報、合言葉などを受け取り
•Redisデータ取得
•絞り込み条件
-同一アプリバージョン
機能やバグに差異が生じ、盤面がズレる原因になるた
め。
-位置情報が近いルーム
上限数になるまで、徐々に距離を延ばして検索を繰り
返す(距離の上限もある)
•ルームのリスト及び表示用データを返す
機能-ルーム検索-
‣ PHP側
• ルームIDなどを受け取り
• Redisデータ更新
メンバー情報に自身を追加
※ソケット未接続ステータス
• ルーム内のユーザのデッキ情報など表示用データを
返す
‣ Node.js側
• ユーザID、ルームIDなどを受け取り
• Redisデータ更新
ソケット接続済みステータスに更新
• 更新情報など表示に使うデータを全メンバーへ通知
機能-ルーム参加-
機能-同期/待ち合わせ-
‣ Node.js側
1.各端末から準備完了イベントを発行
2.自身の状態を準備完了ステータスに更新
3.一定時間応答のないユーザーの切断処理
4.全員が揃うまでは待機を全メンバーへ通知
5.全員が揃ったら同期完了を全メンバーへ通
知
機能-ソケット切断-
‣ Node.js側
• 生存確認イベントが一定時間来ないプレイ
ヤーのソケットを切断
• Redisデータ更新
- ホストが離脱した場合は解散or権限委譲
- ゲストの場合、ホストに権限委譲
• 切断情報を全メンバーに通知
‣ Node.js側
一定時間内の再接続要求は再度ルームに紐
付けし直す。
• ユーザID、ルームIDなどを受け取り
• Redisデータ更新
自身のコネクションIDを書き換える
• 更新情報など表示に使うデータ全メンバー
へ通知
機能-ソケット再接続-
機能-その他イベント-
•生存確認
•なぞり
•クエスト開始通知
•プレイヤーターン終了
•コンティニュー
•リタイヤ
•スキル使用
•コミュニケーション
‣プロセスをCluster構成にできない
【原因】
Redis周りの独自実装が原因。
MemoryStoreを利用してセッション管理をしていたため、
別のプロセスに振られると処理できない。
【一次対応】
1サーバにつき1マスタープロセス、1ワーカプロセスにし
た。
➡︎1コアだったため、別プロセスの影響を受け、Nodeの処
理が詰まることがあった
【恒久対応】
素直にRedisStoreを利用すれば別のサーバ/プロセスに振ら
れても問題なくセッション維持ができる。
(今週リリースします)
発生した問題と対応
LB
①handshake
③connect
④connected
handshakeとconnectが別サーバーへ振られても、コネク
ションが確立できる。
つまりsticky sessionも不要になる。
‣ RedisStoreを利用した場合
②セッション共有
④セッション情報取得
発生した問題と対応
ご静聴ありがとうございました
。

ポコロンダンジョンズとリアルタイム通信 -サーバサイド編-

Editor's Notes

  • #5 ・同じ色のパズルブロック「ポコロン」をなぞってモンスターを攻撃 ・ダンジョンで入手した素材を合成して、武器・防具を作ったり ・モンスターを強化・進化させたり ・マルチプレイは最大4人 ・シングル・マルチともにターン制
  • #6 22日に3.7.0アップデート
  • #8 Redis ・キャッシュ用(非リアルタイム通信) ・データ用(リアルタイム通信) データ用はNode.js側と共有している
  • #9 ・Socket.IOの0.9.16はメモリリーク問題があったが0.9.17で解消されている。 ・Redis ・Pub/Sub用(リアルタイム通信) ・データ用(リアルタイム通信) データ用はPHP側と共有している
  • #11 ピュアにライブラリを利用せず、独自でPub/Subを実装しているため、色々と問題があった。
  • #14 各設定を軽く説明していく
  • #19 socket.ioも同じ設定
  • #20 各ライブラリのバージョン
  • #22 これら以外にもキーはあるがメインはこの4つ。 ・プレイヤーとルームの紐付け用のキーがあるsocket切断で残ってしまったルームを削除するため ・同イベントの複数送信対策など
  • #26 Redisのキーに関しては後ほど説明