ちょっと気になるツイートを見かけたのでそれ絡みで。
https://twitter.com/Sikushima/status/374120078976880640
「艦これ」から、ソーシャル系のサーバ構成を考える – SQLer 生島勘富 の日記
http://d.hatena.ne.jp/Sikushima/20130901/1378021345
艦これサーバーのトラブル云々には同意しかねるのですが、素直にMySQLのSQLで組んだほうが、下手にデータ構造を考えてAPIを作ったりするよりは断然早い、ところの同感です。更新系は兎も角、参照関係は断然RDBMSは早く作られているので、下手のO/RマッピングやるよりもSQL直で書いたほうが手早くかつ実行スピードがすばやくできます。ええ、SQL文を書ければ、の前提なのですが。
で、艦これの同時セッション絡みの部分、いくらか艦これをやっていて、いくらかFlashの通信部分を覗いてみると、さほどセッション/DBコネクション自体には負荷がかかっていないことが分かります。つーか、アクションRPGと違って、艦これの場合、サーバーとの通信が極端に少なくて、ガラ携のWEBゲームぐらい通信は少ない。なので、同時接続数(アクティブ数が30%だとしても3万人ぐらい)だとしても、DBコネクション等で輻輳しているところは更に1/100以下ぐらい、もっと少ないかもしれません。艦これの場合、Flashから各サーバーに送るのは、普通にRESTで送って、JSONで返ってきます。詳しく見ると cookie を使っていません。GET するときの URL に token が入っているのですが、多分ユーザIDへの紐づけかと。なので、非常にDBに負荷の少ない設計(意図的…ということにしておきましょう)になっています。ぷよぷよクエストとか、バウモンとか、レガリアを iPhone でやっていると、やたらに「ログイン中です…」が出るのですが、艦これの場合は全く出てきません。そういう意味では、パズドラも出てこないですね。優秀です。
このあたり、通常のソーシャルゲームにある、
- ユーザーごとのチャット機能
- 他ユーザーのデータを参照して、何かやる(仲間にするとか)する機能
- リアルタイムに他ユーザーと何かやる機能
というのがごっそり抜けています。これは、各鎮守府サーバー(ログイン先のサーバー)間で通信が発生しない(発生できない)のと、まあ、そのあたりを設計しなかっった(意図的に…としておきます)ためです。なので、極端な話で言えば、ロードバランサーすら必要なくて(あるのかな?)、ひとつのサーバーで10万人程度受け持つ、という予算の中で組んで、開いてみたら思わずアクセスがあって、順々にサーバーを増やす、という作りになっているかと。と想像するわけですが、そのあたりは ツチノコブログ | DMM.comラボのインフラエンジニアブログ を参照して頂くということで。
なので、参照系のデータとしては艦船データやマップデータで、更新系は各ユーザーのHPやログインデータぐらいなもので、データベースへの輻輳の率を考えると、それほどシビアな形で使わないくてもうまくいのかな、と想像します。MySQL Clustor を使う位だから、それなりに負荷は掛かっているのでしょうが、同時コネクション数は、100程度あれば十分かも。
■戦闘の開始(battole)と結果(battleresult)
戦闘開始
svdata={ "api_result":1, "api_result_msg":"成功", "api_data":{ "api_dock_id":1, "api_ship_ke":[-1,509,509,502,502,502,-1], "api_ship_lv":[-1,1,1,1,1,1,-1], "api_nowhps":[-1,66,36,36,37,25,43,58,58,22,22,22,-1], "api_maxhps":[-1,75,40,36,37,25,63,58,58,22,22,22,-1], "api_midnight_flag":0, "api_eSlot":[[505,513,525,-1,-1],[505,513,525,-1,-1],[502,-1,-1,-1,-1],[502,-1,-1,-1,-1],[502,-1,-1,-1,-1],[-1,-1,-1,-1,-1]], "api_eKyouka":[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]], "api_fParam":[[82,0,50,81],[3,0,26,25],[32,19,19,28],[39,24,31,30],[24,43,31,17],[74,0,38,69]], "api_eParam":[[32,32,16,28],[32,32,16,28],[7,16,7,6],[7,16,7,6],[7,16,7,6],[0,0,0,0]], "api_search":[1,1], "api_formation":[3,2,1], "api_stage_flag":[1,1,1], "api_kouku":{ "api_plane_from":[[2],[-1]], "api_stage1":{ "api_f_count":58, "api_f_lostcount":0, "api_e_count":0, "api_e_lostcount":0, "api_disp_seiku":1 }, "api_stage2":{ "api_f_count":46, "api_f_lostcount":0, "api_e_count":0, "api_e_lostcount":0 }, "api_stage3":{ "api_frai_flag":[-1,0,0,0,0,0,0], "api_erai_flag":[-1,0,0,1,0,0,0], "api_fbak_flag":[-1,0,0,0,0,0,0], "api_ebak_flag":[-1,0,0,0,0,1,0], "api_fcl_flag":[-1,0,0,0,0,0,0], "api_ecl_flag":[-1,0,0,0,0,0,0], "api_fdam":[-1,0,0,0,0,0,0], "api_edam":[-1,0,0,25,0,0,0] } }, "api_support_flag":0, "api_support_info":null, "api_opening_flag":0, "api_opening_atack":null, "api_hourai_flag":[1,1,0,0], "api_hougeki1":{ "api_at_list":[-1,1,7,6,8,4,3,5,-1,-1,-1,-1,-1], "api_df_list":[-1,11,6,10,2,8,8,7,-1,-1,-1,-1,-1], "api_si_list":[-1,8,505,7,505,6,6,4,-1,-1,-1,-1,-1], "api_cl_list":[-1,1,2,1,1,1,1,1,-1,-1,-1,-1,-1], "api_damage":[-1,85,2,61,16,15,5,5,0,0,0,0,0] }, "api_hougeki2":{ "api_at_list":[-1,1,8,3,4,5,6,-1,-1,-1,-1,-1,-1], "api_df_list":[-1,7,2,8,8,8,8,-1,-1,-1,-1,-1,-1], "api_si_list":[-1,12,505,6,6,4,11,-1,-1,-1,-1,-1,-1], "api_cl_list":[-1,1,0,1,1,1,2,-1,-1,-1,-1,-1,-1], "api_damage":[-1,66,0,4,8,2,69,0,0,0,0,0,0] }, "api_hougeki3":null, "api_raigeki":null } }
戦闘開始のAPIを呼び出すと、いきなり戦闘結果が返ってきます。api_hougeki1 と api_hougeki2 のあたりがそれですね。サーバーにはユーザーの艦娘のデータが保持されているので、鎮守府サーバーで戦闘の計算を出して返してきます。なので、戦闘をしてズル…はできないと思います。api_hougeki1 などの結果で、クライアントの Flash でアニメーションをしています。ということは、このあたりのデータを解析すれば、戦闘シーンは復元可能ってことです。
戦闘結果
svdata={ "api_result":1, "api_result_msg":"成功", "api_data":{ "api_ship_id":[-1,509,509,502,502,502,-1], "api_win_rank":"S", "api_get_exp":60, "api_mvp":1, "api_member_lv":24, "api_member_exp":29431, "api_get_base_exp":120, "api_get_ship_exp":[-1,432,144,144,144,144,144], "api_get_exp_lvup":[[69933,70300,74100],[2471,2800],[2574,2800],[8773,9100],[9330,10500],[12372,13600]], "api_dests":5, "api_destsf":1, "api_lost_flag":[-1,0,0,0,0,0,0], "api_quest_name":"カムラン半島", "api_quest_level":3, "api_enemy_info":{ "api_level":"", "api_rank":"", "api_deck_name":"敵前衛部隊" }, "api_first_clear":0, "api_get_flag":[0,1,0], "api_get_ship":{ "api_ship_id":32, "api_ship_type":"駆逐艦", "api_ship_name":"初雪", "api_ship_getmes":"初雪……です……よろしく。" }, "api_get_eventflag":0 } }
戦闘のアニメーションが終わったら、戦闘結果≒経験値を取得します。この計算も既に鎮守府サーバーで行った結果を返してきて、結果のアニメーションを出します。新しい艦娘が出て来たときには、この JSON に書いてあります。ランクとか経験値が書いてあるので、先の戦闘前の戦闘結果(ってややこしい)と、経験値の結果をあわせると、自分の艦娘の戦歴が作成できます。
多分、サーバーのほうでは、この手の履歴は保持ってないと思うんですよね。なので、いつ、どんな風に戦闘を勝ち抜いてきたのか、という戦歴は、この方法でしか取れないと思います。
■敷波復活
そんな訳で、この通信キャプチャを遣っている間に「敷波さん」をGetッ!!! 英霊により復活であります。
まだ、Get したばっかりなので、レベル1ですが、ええ、「ブラ鎮」≒「鎮守府をぶらぶら歩くこと」(銀ぶらと同じですねッ!!! 決して、ブラック鎮守府ではありませぬー)して遠征に出れる位までには御育て致しましょう。
2014/03/11 追記
艦これのバトルJSONを再解析(祥鳳改小破編) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/5517