Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cygamesを支えるPHPと、その高速化の取り組み

Cygames
November 08, 2016

 Cygamesを支えるPHPと、その高速化の取り組み

2016/11/03 PHPカンファレンス 2016

Cygames

November 08, 2016
Tweet

More Decks by Cygames

Other Decks in Technology

Transcript

  1. ©  Cygames,  Inc. 今回お話したいこと • 前半 • Cygamesのサーバー構成/技術スタック • コンテンツ開発に対してのマインドセット

    • 負荷分散/⾼高速化/リリースでの取り組み • 後半 • ⾼高速化のためZephir導⼊入検討 • Zephirの概要と導⼊入 • 運⽤用してみてとこれから
  2. ©  Cygames,  Inc. ⾃自⼰己紹介 • ⼩小笠笠原空宙(たかひろ) • サーバーサイドエンジニーア • ほぼPHP

    • ちょっとperl • 2014年年7⽉月 Cygames join • その前はモバイルサイト/ソーシャルゲーム開発
  3. ©  Cygames,  Inc. ©  Cygames,  Inc. MySQLに変わるDB 「 CySQL 」を開発中!

    •シャドーバースポータルで ⼀一部稼働済み
  4. ©  Cygames,  Inc. とあるアプリの現状 • ⽉月間300億PV • ピーク時、秒間5万アクセス • ピーク時、秒間100万クエリ

    • 1⽇日のDBのトランザクションデータ増加量量 • 1〜~2TB • 1⽇日のログデータ増加量量 • 60GB〜~
  5. ©  Cygames,  Inc. Cygamesを⽀支えるサーバーサイド • サーバー構成/技術スタック • 何を、どのように利利⽤用しているのか • 開発スタイル/マインドセット

    • どのような価値観で開発しているか • 負荷分散/⾼高速化/リリースでの取り組み • 開発~∼運⽤用までの取り組み • 今/これからの取り組み • 今後どう前進しようとしているか
  6. ©  Cygames,  Inc. CS(カスタマーサポート)最優先 • お客様からの問い合わせには即対応 • 即対応できるようなログの設計 • ログの可視化ツールの構築

    • 本番環境のデータを複製し、実機で動きを確認 できる検証環境 • 確証を持って対応できるように
  7. ©  Cygames,  Inc. 当たり前のことを当たり前にやる • ⼩小さな差がピーク時に⼤大きな差に • 不不必要な処理理は書かない • 不不必要なデータは取得しない

    • 適切切なリファクタリング • 運⽤用し易易い実装、拡張しやすい実装 • ⽇日々の積み重ねが、 良良くも悪くもピーク時に顕在化する
  8. ©  Cygames,  Inc. 当たり前のことを当たり前にやる • 当たり前のレベルがスピードにつながる • ほぼ毎⽇日複数回デプロイ • ほぼ常にイベント開催

    • 新機能追加、機能改善、etc • PJに関わる⼈人間も多くなる • 当たり前のことを増やし、積み上げることで チームのレベルを上げていく
  9. ©  Cygames,  Inc. 負荷分散/スケールアウトの取り組み • DBは⽔水平、垂直分割 • キャッシュはTwemproxyで分散 • Node.jsはNginxでバランシング

    • 各種ログは⾮非同期で保存 • バッチ処理理できるものはバッチ処理理へ • 分散/スケールアウトできるように • できるだけキャッシュ • 同期処理理は必要最低限に
  10. ©  Cygames,  Inc. プロファイリングの取り組み • 「推測するな、計測せよ」 • New  relicで解析 •

    以前はxhprofなど • 重いクエリ、最適化できる処理理などを地道に対応 • FW側の不不要な処理理なども最適化 • 4.5秒以上かかったリクエストを⽇日次で集計、通知 • 検知 ⇔  最適化の地道な対応 イベント開始><; 負荷対策リリース\(^o^)/ レスポンスタイム推移
  11. ©  Cygames,  Inc. DB関連での取り組み • ボトルネックの多くはDB関連 • トランザクション中の処理理は最⼩小限に • まとめられるクエリはまとめる

    • indexはカーディナリティが⾼高い順に • ⽇日次で開発環境で実⾏行行された全クエリをexplain • 問題のありそうなクエリは通知
  12. ©  Cygames,  Inc. DB関連での取り組み • 数⼗十GBのテーブルを安全にdropする • データファイルが⼤大きいとファイル削除時、 io_̲waitで数秒間ではあるがレスポンス劣劣化 i.

    drop対象のデータファイルにhard  link貼る ii. drop  table実⾏行行 iii. hard  linkしたファイルのサイズを切切り詰めた後 に、remove(linuxのtruncateコマンド) • ゆるやかに削除することで数百テーブル、合計数百 GBのテーブル削除もレスポンス劣劣化なく実⾏行行
  13. ©  Cygames,  Inc. リリース時の取り組み • リリース前には職種/チーム問わずレビュー • 全スタッフ ≒  ゲームユーザ

    • プランナー/エンジニア/デザイナ/バックオフィス • ⾯面⽩白いと確信が持てるまでブラッシュアップ • リリース前には実際に運⽤用テスト • 各種更更新作業などの通常運⽤用リハーサル • 緊急デプロイなどの障害対応フローなども確認
  14. ©  Cygames,  Inc. 今/これからの取り組み • PHP7対応 • FWのcoreを拡張させている箇所の対応 • 各種extensionの対応状況の調査

    • 品質と⽣生産性の向上、仕組みづくり • ゲーム特有の問題を検知できる仕組み • ユニット/E2Eテストだけではない何か • Zephirによる⾼高速化/共通化 • この後詳しく・・・
  15. ©  Cygames,  Inc. Zephir検討への経緯 • レスポンスの⾼高速化は最優先事項の1つ • クエリ/処理理最適化もある程度度やった感 • (まだまだ最適化できることはあるけど…)

    • 違う⾓角度度からの最適化を⾏行行いたい ミドルウェアのバージョン 上げようぜ! フレームワーク変えようぜ!
  16. ©  Cygames,  Inc. Zephir検討への経緯 • レスポンスの⾼高速化は最優先事項の1つ • クエリ/処理理最適化もある程度度やった感 • (まだまだ最適化できることはあるけど…)

    • 違う⾓角度度からの最適化を⾏行行いたい ミドルウェアのバージョン 上げようぜ! フレームワーク変えようぜ! ⾔言語変えようぜ!
  17. ©  Cygames,  Inc. Zephir検討への経緯 • レスポンスの⾼高速化は最優先事項の1つ • クエリ/処理理最適化もある程度度やった感 • (まだまだ最適化できることはあるけど…)

    • 違う⾓角度度からの最適化を⾏行行いたい ミドルウェアのバージョン 上げようぜ! フレームワーク変えようぜ! ⾔言語変えようぜ! ⾯面⽩白くなければ意味がない エンジニア的にはやってみたいんだがしかし・・・
  18. ©  Cygames,  Inc. Zephir検討への経緯 • レスポンスの⾼高速化は最優先事項の1つ • クエリ/処理理最適化もある程度度やった感 • (まだまだ最適化できることはあるけど…)

    • 違う⾓角度度からの最適化を⾏行行いたい ミドルウェアのバージョン 上げようぜ! フレームワーク変えようぜ! ⾔言語変えようぜ! ⾯面⽩白くなければ意味がない エンジニア的にはやってみたいんだがしかし・・・ Zephirっていうのが あるらしいぞい
  19. ©  Cygames,  Inc. Zephirとは • PHPerでも簡単にextensionが書ける⾔言語 • PHPのような記述でextensionが作成できる • if⽂文の書式が違う

    • 変数に$使わない • 変数上書き時は、let  を使う • 静的/動的型⾔言語 • メモリ管理理等が必要ない • ⾼高速FW PhalconはZephirで実装
  20. ©  Cygames,  Inc. Zephirについて • Hello  World namespace  Utils; class

     Hello { //say  func public  static  function  say() { echo  "Hello  World!"; } } <?php Utils¥Hello::say();    //  prints  Hello  World! $zephir build
  21. ©  Cygames,  Inc. Extensionが⽣生成/実⾏行行までのフロー 1. Zephirのスケルトンを作成 $zephir init utils utils

    ├──  ext //Zephirのcore,⽣生成されるextensionなどが配置される │ └──  utils //ここに.zepファイルを作成していく
  22. ©  Cygames,  Inc. Extensionが⽣生成/実⾏行行までのフロー 3.Zephirでextension⽣生成→compile • ZephirがPHPで.zepファイルを解析 • C⾔言語のextensionファイル群⽣生成 •

    gccでコンパイル、.soファイルをext_̲dirにコピー $zephir build Compiling... Installing... Extension  installed! Don't  forget  to  restart  your  web  server hello.zep C⾔言語 utils ext ファイル群 utils.so PHP gcc
  23. ©  Cygames,  Inc. どんなソースが⽣生成される? • Zephirでフィボナッチ数列列 • 定義通り再帰でやってみる class  Fib

    { public  function  calc(int n)  -‐‑‒>  int { if  n  <  2  { return  n; } return  self::calc(n  -‐‑‒ 1)  +  self::calc(n  -‐‑‒ 2); } }
  24. ©  Cygames,  Inc. どんなソースが⽣生成される? PHP_̲METHOD(Utils_̲Fib,  calc)  { zval *n_̲param =

     NULL,  *_̲0  =  NULL,  *_̲1  =  NULL,  *_̲2  =  NULL; int n,  ZEPHIR_̲LAST_̲CALL_̲STATUS; ZEPHIR_̲MM_̲GROW(); zephir_̲fetch_̲params(1,  1,  0,  &n_̲param); n  =  zephir_̲get_̲intval(n_̲param); if  (n  <  2)  { RETURN_̲MM_̲LONG(n);} ZEPHIR_̲INIT_̲VAR(_̲1); ZVAL_̲LONG(_̲1,  (n  -‐‑‒ 1)); ZEPHIR_̲CALL_̲SELF(&_̲0,  "calc",  NULL,  0,  _̲1); zephir_̲check_̲call_̲status(); ZEPHIR_̲INIT_̲NVAR(_̲1); ZVAL_̲LONG(_̲1,  (n  -‐‑‒ 2)); ZEPHIR_̲CALL_̲SELF(&_̲2,  "calc",  NULL,  0,  _̲1); zephir_̲check_̲call_̲status(); zephir_̲add_̲function(return_̲value,  _̲0,  _̲2); RETURN_̲MM(); }
  25. ©  Cygames,  Inc. どんなソースが⽣生成される? PHP_̲METHOD(Utils_̲Fib,  calc)  { zval *n_̲param =

     NULL,  *_̲0  =  NULL,  *_̲1  =  NULL,  *_̲2  =  NULL; int n,  ZEPHIR_̲LAST_̲CALL_̲STATUS; ZEPHIR_̲MM_̲GROW(); zephir_̲fetch_̲params(1,  1,  0,  &n_̲param); n  =  zephir_̲get_̲intval(n_̲param); if  (n  <  2)  { RETURN_̲MM_̲LONG(n);} ZEPHIR_̲INIT_̲VAR(_̲1); ZVAL_̲LONG(_̲1,  (n  -‐‑‒ 1)); ZEPHIR_̲CALL_̲SELF(&_̲0,  "calc",  NULL,  0,  _̲1); zephir_̲check_̲call_̲status(); ZEPHIR_̲INIT_̲NVAR(_̲1); ZVAL_̲LONG(_̲1,  (n  -‐‑‒ 2)); ZEPHIR_̲CALL_̲SELF(&_̲2,  "calc",  NULL,  0,  _̲1); zephir_̲check_̲call_̲status(); zephir_̲add_̲function(return_̲value,  _̲0,  _̲2); RETURN_̲MM(); } int型で⽐比較を ⾏行行っている
  26. ©  Cygames,  Inc. どんなソースが⽣生成される? PHP_̲METHOD(Utils_̲Fib,  calc)  { zval *n_̲param =

     NULL,  *_̲0  =  NULL,  *_̲1  =  NULL,  *_̲2  =  NULL; int n,  ZEPHIR_̲LAST_̲CALL_̲STATUS; ZEPHIR_̲MM_̲GROW(); zephir_̲fetch_̲params(1,  1,  0,  &n_̲param); n  =  zephir_̲get_̲intval(n_̲param); if  (n  <  2)  { RETURN_̲MM_̲LONG(n);} ZEPHIR_̲INIT_̲VAR(_̲1); ZVAL_̲LONG(_̲1,  (n  -‐‑‒ 1)); ZEPHIR_̲CALL_̲SELF(&_̲0,  "calc",  NULL,  0,  _̲1); zephir_̲check_̲call_̲status(); ZEPHIR_̲INIT_̲NVAR(_̲1); ZVAL_̲LONG(_̲1,  (n  -‐‑‒ 2)); ZEPHIR_̲CALL_̲SELF(&_̲2,  "calc",  NULL,  0,  _̲1); zephir_̲check_̲call_̲status(); zephir_̲add_̲function(return_̲value,  _̲0,  _̲2); RETURN_̲MM(); } intではなく、 ZVALで値の やり取りをしている
  27. ©  Cygames,  Inc. どんなソースが⽣生成される? PHP_̲METHOD(Utils_̲Fib,  calc)  { zval *n_̲param =

     NULL,  *_̲0  =  NULL,  *_̲1  =  NULL,  *_̲2  =  NULL; int n,  ZEPHIR_̲LAST_̲CALL_̲STATUS; ZEPHIR_̲MM_̲GROW(); zephir_̲fetch_̲params(1,  1,  0,  &n_̲param); n  =  zephir_̲get_̲intval(n_̲param); if  (n  <  2)  { RETURN_̲MM_̲LONG(n);} ZEPHIR_̲INIT_̲VAR(_̲1); ZVAL_̲LONG(_̲1,  (n  -‐‑‒ 1)); ZEPHIR_̲CALL_̲SELF(&_̲0,  "calc",  NULL,  0,  _̲1); zephir_̲check_̲call_̲status(); ZEPHIR_̲INIT_̲NVAR(_̲1); ZVAL_̲LONG(_̲1,  (n  -‐‑‒ 2)); ZEPHIR_̲CALL_̲SELF(&_̲2,  "calc",  NULL,  0,  _̲1); zephir_̲check_̲call_̲status(); zephir_̲add_̲function(return_̲value,  _̲0,  _̲2); RETURN_̲MM(); } 再帰部分の 関数コール
  28. ©  Cygames,  Inc. どんなソースが⽣生成される? PHP_̲METHOD(Utils_̲Fib,  calc)  { zval *n_̲param =

     NULL,  *_̲0  =  NULL,  *_̲1  =  NULL,  *_̲2  =  NULL; int n,  ZEPHIR_̲LAST_̲CALL_̲STATUS; ZEPHIR_̲MM_̲GROW(); zephir_̲fetch_̲params(1,  1,  0,  &n_̲param); n  =  zephir_̲get_̲intval(n_̲param); if  (n  <  2)  { RETURN_̲MM_̲LONG(n);} ZEPHIR_̲INIT_̲VAR(_̲1); ZVAL_̲LONG(_̲1,  (n  -‐‑‒ 1)); ZEPHIR_̲CALL_̲SELF(&_̲0,  "calc",  NULL,  0,  _̲1); zephir_̲check_̲call_̲status(); ZEPHIR_̲INIT_̲NVAR(_̲1); ZVAL_̲LONG(_̲1,  (n  -‐‑‒ 2)); ZEPHIR_̲CALL_̲SELF(&_̲2,  "calc",  NULL,  0,  _̲1); zephir_̲check_̲call_̲status(); zephir_̲add_̲function(return_̲value,  _̲0,  _̲2); RETURN_̲MM(); } ZVALなので intの⾜足し算も関数 コールになっている
  29. ©  Cygames,  Inc. Zephirでログ出⼒力力 • fwrite()でtsv形式でファイル出⼒力力 $fp =  fopen($file,  "a");

    fwrite($fp,  implode("¥t",  $msg_̲arr).PHP_̲EOL); fclose($fp); public  function  write(string  file,  array  msg_̲arr) { var fp =  fopen(file,  "a"); fwrite(fp,  implode("¥t",  msg_̲arr).PHP_̲EOL); fclose(fp); }
  30. ©  Cygames,  Inc. ZephirでNGワードチェック • NGワード配列列をループさせてstrpos()で検索索 • NGワードは50語 public  $ng_̲list=

     [“ダメな⾔言葉葉”,“いけない⾔言葉葉”,“やばい⾔言葉葉",…]; public  function  check($str) { foreach ($this-‐‑‒>ng_̲list as  $key  =>  $value)  { if  (strpos($str,  $value)  !==  false)  { $is_̲ng =  true; //break;  ⽐比較のためbreakはしない } } }
  31. ©  Cygames,  Inc. ⼀一旦ここまでわかったこと • PHPとの互換性のためZVALでの計算など、 実質PHPがやっていることと同様の事を⾏行行って いる様⼦子。C⾔言語的に最適化の余地はある • 配列列処理理などループ処理理などは速くなる

    • Zephirで標準関数のいくつか⾼高速化されている • PHPのコードをほぼ変えずに、Zephirの構⽂文 規則に整えればextension化ができる • 引数の型などはチェックは確実に⾏行行う • PHPだとwarningでもFATAL  ERRORになる
  32. ©  Cygames,  Inc. 改めてZephir/extension化すること • Pros • ⾼高速化できる • PJ/FWなど関係なく利利⽤用可能

    • 特定箇所のみに適応できる • PHPに戻すことも可能 • Cons • デプロイフローが増える • 修正がしにくい
  33. ©  Cygames,  Inc. 全体まとめ • Cygamesは、 最⾼高のコンテンツを作りたいと思っています • それには⾼高速なトライ&エラーが⽋欠かせない •

    同時に⾼高速なレスポンスも⽋欠かせない • それらを両⽴立立させるためPHPを使っています!