PHPカンファレンス福岡2017の資料です
1,800リクエスト/秒の世界The world of 1,800 requests / sec長谷川智希HASEGAWA Tomoki
View Slide
長谷川 智希デジタルサーカス株式会社 副団長CTODigital Circus, Inc. Vice-master CTOTokyo, Japan@tomzoh
ライフワーク:Web / iOSアプリ開発, ビール, 電子工作, サッカー観戦, レンタルカートレース, …長谷川 智希Web / iOS App Development, Beer, IoT,Watch soccer match, Rental Kart Racing, …デジタルサーカス株式会社 副団長CTODigital Circus, Inc. Vice-master CTOTokyo, JapanLifeworks:@tomzoh
WE ARE HIRING!!Web DevelopmentMobile App Development( )(iOS, Android)http://www.dgcircus.comOmotesando, Tokyo
今日のテーマThe world of 1,800 requests / sec1,800リクエスト/秒の世界Today’s theme
システム概要
今回の対象システム• Web + API + iOS/Android アプリ• 就職活動関連アプリ• ユーザ数: 100万前後
システム構成Ȑ Ȑ2 × m4.largevCPU: 2 / memory: 8GBEC2 RDSdb.m3.xlargevCPU: 4 / memory: 7.5GBiOS / Android Mobile Apps1,000,000users
技術スタック
技術スタック今日の話
負荷がやってくる
なぜわかっている?
03/01 0:00 就職活動解禁
負荷• 負荷がやってくる
負荷• 負荷がやってくる• 耐えられる様にしたい• 負荷を想定• 耐えられそうな仮構成を作る• 負荷を作り出して構成を調整
負荷• 負荷がやってくる• 耐えられる様にしたい• 負荷を想定• 耐えられそうな仮構成を作る• 負荷を作り出して構成を調整• どうやって耐えるか• Webサーバ(EC2)のスケールアウトで対応したい• DB(RDS)がツラければスケールアップする
• 耐えられる様にしたい• 負荷を想定• 耐えられそうな仮構成を作る• 負荷を作り出して構成を調整負荷• 負荷がやってくる• どうやって耐えるか• Webサーバ(EC2)のスケールアウトで対応したい• DB(RDS)がツラければスケールアップする
負荷の計算に使える材料
負荷の計算• 負荷の計算に使える素材• サイト全体のWebでのページビュー/秒• ネイティブアプリのユーザ比率
負荷の計算• 負荷の計算に使える素材• サイト全体のWebでのページビュー/秒• ネイティブアプリのユーザ比率10,000PV/sec
負荷の計算• 負荷の計算に使える素材• サイト全体のWebでのページビュー/秒• ネイティブアプリのユーザ比率10,000PV/sec30%
負荷の計算• 負荷の計算に使える素材• サイト全体のWebでのページビュー/秒• ネイティブアプリのユーザ比率10,000PV/sec30%×
\3,000 PV/sec/
\3,000 PV/sec/ざわ...ざわ...
まあ待て
落ち着け
Ȑ
ȐAPI
PV APIコール
PV APIコール≠
PV APIコール≠変換
PV → API• ユーザがどういう行動を取るか• シナリオを作成• シナリオの各ステップで発生するAPIコールをリストアップ• シナリオの各ステップから、同じ動作をWebで実施した場合に発生するPVに変換• PV → APIコールの変換ができる
シナリオNo. 操作ユーザ操作処理時間仮想PV1 アプリ初回起動 12 ログイン画面からのログイン処理 10 1 23 ホーム表示 2 1 14 検索タブ表示 2 15 検索 10 2 36 検索結果一覧画面表示 17 (詳細画面 → エントリー)x 30 3,000 120 120
シナリオとその割合シナリオユーザ操作処理時間仮想PV創出PV/秒シナリオ割合必要PV/秒同時セッションシナリオ1 3,022 127 128 0.04 20% 600 14,761シナリオ2 27 10 8 0.22 40% 1,200 5,550シナリオ3 21 11 5 0.16 35% 1,050 6,720シナリオ4 20 10 12 0.40 5% 150 375
シナリオとその割合シナリオユーザ操作処理時間仮想PV創出PV/秒シナリオ割合必要PV/秒同時セッションシナリオ1 3,022 127 128 0.04 20% 600 14,761シナリオ2 27 10 8 0.22 40% 1,200 5,550シナリオ3 21 11 5 0.16 35% 1,050 6,720シナリオ4 20 10 12 0.40 5% 150 3753,000PV/sec
シナリオが出来た
システム構成Ȑ Ȑ2 × m4.largevCPU: 2 / memory: 8GBEC2 RDSdb.m3.xlargevCPU: 4 / memory: 7.5GB
システム構成Ȑ Ȑ2 × m4.largevCPU: 2 / memory: 8GBEC2 RDSdb.m3.xlargevCPU: 4 / memory: 7.5GBȐ Ȑ16 × m4.largevCPU: 2 / memory: 8GBEC2 RDSdb.m3.xlargevCPU: 4 / memory: 7.5GBȐ Ȑ Ȑ ȐȐ ȐȐ ȐȐ Ȑ Ȑ ȐȐ Ȑ
Apacheをちゃんと設定する• Apacheのプロセス数をいくつにする?
Apacheをちゃんと設定する• Apacheのプロセス数をいくつにする?$ ps auxUSER PID %CPU %MEM VSZ RSS TTYwww-data 11270 0.0 0.3 441636 26720 ?STAT START TIME COMMANDS 06:25 0:02 /usr/sbin/apache2 -k start
Apacheをちゃんと設定する• Apacheのプロセス数をいくつにする?$ ps auxUSER PID %CPU %MEM VSZ RSS TTYwww-data 11270 0.0 0.3 441636 26720 ?STAT START TIME COMMANDS 06:25 0:02 /usr/sbin/apache2 -k startȐm4.largevCPU: 2 / memory: 8GB
Apacheをちゃんと設定する• Apacheのプロセス数をいくつにする?$ ps auxUSER PID %CPU %MEM VSZ RSS TTYwww-data 11270 0.0 0.3 441636 26720 ?STAT START TIME COMMANDS 06:25 0:02 /usr/sbin/apache2 -k startȐm4.largevCPU: 2 / memory: 8GB8GB中1.6GB(20%)くらいOSに…。残り80%(6.4GB)をApacheに。
Apacheをちゃんと設定する• Apacheのプロセス数をいくつにする?$ ps auxUSER PID %CPU %MEM VSZ RSS TTYwww-data 11270 0.0 0.3 441636 26720 ?STAT START TIME COMMANDS 06:25 0:02 /usr/sbin/apache2 -k startȐm4.largevCPU: 2 / memory: 8GB8GB中1.6GB(20%)くらいOSに…。残り80%(6.4GB)をApacheに。80% / 0.4% = 200
Apache設定Timeout 10KeepAliveTimeout 120StartServers 200MinSpareServers 200MaxSpareServers 200ServerLimit 200MaxClients 200
負荷メーカーと言えば彼
JMeterでシナリオを実装する
JMeterでシナリオを実装するシナリオ ウェイト処理時間仮想PV創出PV/secシナリオ割合必要PV/秒同時セッションシナリオ2 27 10 8 0.22 40% 1,200 5,550
シナリオが実装できた
よし負荷テストだ
実行!!
/ファー\実行!!
Macのファン全開
Macのファン全開• 今回欲しかった並行実行数は27,000セッション。• Mac 1台では負荷をかけきれない。
どうしたか
スケールアウト
システム構成Ȑ Ȑ16 × m4.largevCPU: 2 / memory: 8GBEC2 RDSdb.m3.xlargevCPU: 4 / memory: 7.5GBȐ Ȑ Ȑ ȐȐ ȐȐ ȐȐ Ȑ Ȑ ȐȐ Ȑ
システム構成Ȑ Ȑ16 × m4.largevCPU: 2 / memory: 8GBEC2 RDSdb.m3.xlargevCPU: 4 / memory: 7.5GBȐ Ȑ Ȑ ȐȐ ȐȐ ȐȐ Ȑ Ȑ ȐȐ ȐȐ Ȑ5 × m4.largevCPU: 2 / memory: 8GBEC2Ȑ Ȑ Ȑ
さて本格負荷テストだ
…
……
まったく捌けない
予想外にキツイ
予想外にキツイ• 負荷をかけ始めた瞬間にレスポンスが悪化し、 ELBから外される。• すべてのWebサーバがELBから外されて ゲームオーバー。• 5分と保たない。
どうしよう
対策を考えよう
まず起動プロセスを整理
APIコールȐ
APIコールȐ端末登録
APIコールȐ端末登録デバイストークン登録
APIコールȐ端末登録デバイストークン登録システム設定取得
APIコールȐ端末登録デバイストークン登録システム設定取得URLホワイトリスト取得
APIコールȐ端末登録デバイストークン登録システム設定取得URLホワイトリスト取得お知らせ取得
負荷対策• APIの特徴: 多くは参照系かつ全員同じ。
負荷対策• APIの特徴: 多くは参照系かつ全員同じ。 →静的ファイル化してしまおう• CakePHPを使っている• app/webroot 配下にファイルを置けばApacheだけでリクエスト & レスポンスが完了する。• CakePHPが実行されないので(PHP比)超高速に処理可能。
どうなったか
爆★速
Ȑシステム設定取得
静的ファイル化• いくつかのAPIを静的ファイル化 → 現実的な速度が出る様になった。• 副作用: 管理画面から値を変更してもAPIに反映されない• この高負荷体制は数日間の時限なのであまり問題にならない。
負荷テスト完了
• Webサーバ、16 x m4.xlarge で大丈夫そう。• 結局、4 x m4.4xlarge にした。• Apacheのプロセス数は2,116。• 大丈夫 = Apacheがエラーを吐かない• エラーを吐くとELBからはずされてサービスダウン負荷テストの結果
• Webサーバ、16 x m4.xlarge で大丈夫そう。• 結局、4 x m4.4xlarge にした。• Apacheのプロセス数は2,116。• 大丈夫 = Apacheがエラーを吐かない• エラーを吐くとELBからはずされてサービスダウン負荷テストの結果お得
よし、これで万全!
そんなふうに考えていた時期が俺にもありました
さて当日
タイムスケジュール2/28 10:00 サイトクローズ・アプリクローズ16:00 PUSH通知3/01 00:00 サイトオープン・アプリオープン01:00 PUSH通知11:00 アプリアップデート強制ON2/2712:00 PUSH通知
アプリアップデート強制ONPUSH通知
アプリアップデート強制ONPUSH通知天井張り付き
むむっ!?
タイムスケジュール2/28 10:00 サイトクローズ・アプリクローズ16:00 PUSH通知3/01 00:00 サイトオープン・アプリオープン01:00 PUSH通知11:00 アプリアップデート強制ON2/2718:00 API静的化12:00 PUSH通知
アプリアップデート強制ONPUSH通知API静的化
ひと安心
アプリクローズȐ端末登録デバイストークン登録システム設定取得
アプリクローズȐ端末登録デバイストークン登録システム設定取得「アプリクローズ」
まあまあ…落ち着いた
2/289:00JST15:00JST21:00JST9:00JST3/13:00JSTサイト・アプリオープンRDS
2/289:00JST15:00JST21:00JST9:00JST3/13:00JSTサイト・アプリオープン0:03JST 1,800リクエスト/秒RDS
昼になっても落ち着かない…
対策するか…
APIコールȐ端末登録デバイストークン登録システム設定取得URLホワイトリスト取得お知らせ取得静的静的静的
APIコールȐ端末登録デバイストークン登録システム設定取得URLホワイトリスト取得お知らせ取得静的静的静的動的動的
APIコールȐ端末登録デバイストークン登録システム設定取得URLホワイトリスト取得お知らせ取得静的静的静的動的動的初回のみ
APIコールȐ端末登録デバイストークン登録システム設定取得URLホワイトリスト取得お知らせ取得静的静的静的動的動的初回のみ起動のたび
デバイストークン登録API、ユーザを探すSQLが遅いな…
デバイストークン登録API、ユーザを探すSQLが遅いな…検索キーにインデックスが無い…
デバイストークン登録API、ユーザを探すSQLが遅いな…検索キーにインデックスが無い…開発環境で…
mysql> explain select * from users where *****************************;+----+-------------+-------+------------+------+---------------+------+---------+-| id | select_type | table | partitions | type | possible_keys | key | key_len |+----+-------------+-------+------------+------+---------------+------+---------+-| 1 | SIMPLE | users | NULL | ALL | NULL | NULL | NULL |+----+-------------+-------+------------+------+---------------+------+---------+-1 row in set, 1 warning (0.00 sec)mysql> select id from users where *****************************;+----+| id |+----+| 1 |+----+1 row in set (0.25 sec)
mysql> explain select * from users where *****************************;+----+-------------+-------+------------+------+---------------+------+---------+-| id | select_type | table | partitions | type | possible_keys | key | key_len |+----+-------------+-------+------------+------+---------------+------+---------+-| 1 | SIMPLE | users | NULL | ALL | NULL | NULL | NULL |+----+-------------+-------+------------+------+---------------+------+---------+-1 row in set, 1 warning (0.00 sec)mysql> select id from users where *****************************;+----+| id |+----+| 1 |+----+1 row in set (0.25 sec)フルスキャンになってて0.25secかかってる…。インデックス足してみよう。
mysql> explain select * from users where *****************************;+----+-------------+-------+------------+------+---------------+------------+---------+-| id | select_type | table | partitions | type | possible_keys | key | key_len |+----+-------------+-------+------------+------+---------------+------------+---------+-| 1 | SIMPLE | users | NULL | ref | index_hash | index_hash | 162 |+----+-------------+-------+------------+------+---------------+------------+---------+-1 row in set, 1 warning (0.00 sec)mysql> select id from users where *****************************;+----+| id |+----+| 1 |+----+1 row in set (0.00 sec)
mysql> explain select * from users where *****************************;+----+-------------+-------+------------+------+---------------+------------+---------+-| id | select_type | table | partitions | type | possible_keys | key | key_len |+----+-------------+-------+------------+------+---------------+------------+---------+-| 1 | SIMPLE | users | NULL | ref | index_hash | index_hash | 162 |+----+-------------+-------+------------+------+---------------+------------+---------+-1 row in set, 1 warning (0.00 sec)mysql> select id from users where *****************************;+----+| id |+----+| 1 |+----+1 row in set (0.00 sec)効果絶大やんけ…。
本番に足したろ。
本番に足したろ。mysql> alter table users add index …
本番に足したろ。mysql> alter table users add index …返ってこない…。しかし Ctrl + C も怖い。しばらく置いておこう…。
本番に足したろ。mysql> alter table users add index …返ってこない…。しかし Ctrl + C も怖い。しばらく置いておこう…。DBサーバの負荷がむっちゃ下がったんですが、何か起きてますか!?
RDS
インデックス追加完了RDS
むっちゃサックサクに
めでたい!
まとめ
まとめ• 1,800リクエスト/秒の世界を体験した。• 負荷試験 & 対策• 負荷をかけるのにも工夫が必要。• Apacheのエラーレートだけでなくサーバの リソース利用状況もチェックするべき。• チューニング• Apacheで完結するアクセスは爆速。• レコード数が多いテーブルではインデックス特に大事。
あたりまえのことをあたりまえに
ThanksWE ARE HIRING@tomzohプリーズ:#phpconfuk / ブログ / 懇親会
AWS / EC2の注意点• いろいろなものに制限がある。• EC2インスタンス数• IPアドレス数• どちらも申請すると上げてもらえるけど1〜2営業日必要• 負荷テストをする時にも申請が必要• これも1〜2営業日
告知
builderscon tokyo 20172017.08.3(木)〜5(土)https://builderscon.io/tokyo/2017 チケット販売中
iOSDC Japan 2017https://iosdc.jp/2017/ スポンサー募集中2017.09.15(金)〜17(日)
PHPerKaigi 2018https://phperkaigi.jpスポンサー募集…するかも2018.03.10(土)• PHPerのためのカンファレンス• 東京都練馬区 Coconeriホールにて• 朝から晩までテックトーク• 夕方からはビールを飲みながら• 夜はクラフトビールで懇親会