マイクロサービスをつくった。
一からマイクロサービスの開発フローを作った話
そこで、実際にどのように開発していたかを共有する。
今回は
- テスト
- ドキュメント作成
- エラー/負荷対策、負荷テスト (←今回はここ)
今回は負荷対策に関して。
開発するときにどのような点に気をつけて、
どのようにテストしたかを書く。
前提として、過去の開発で負荷が起こるのって以下のどれかだった。
どれも起こらないようにするために、どういう開発にしたか、というのを書く。
負荷対策
負荷が大きくなる原因
基本的にサーバ負荷はよほどループを挟んだり、メモリリークがなければ、なんとかなることが多い。
サーバのループ数に関しては、コードチェック時に確認しているので、大きな問題は起こらないだろうと予想。
これまでの負荷による障害は、多くの場合はDBが原因。
DBの中でも、以下のパターンが多い。
- 走査するデータの量が大きい
- INDEXを貼られていないデータを検索している
- パーティションが適切に設定されていない
- 実行するクエリが多い
- キャッシュが効果的に利用されていない
負荷対策のため開発で確認するログ
indexをはられていないクエリの確認
INDEXがはられていないカラムで検索された場合、
SLOW QUERYとして出力されるようにする。
[mysqld]
long_query_time=1
log-queries-not-using-indexes
log-slow-queries=/var/log/mysql/log-slow-queries.log
実行されたクエリの確認
同じクエリを複数回走らせていないかを確認する。
同じクエリを走らせている場合は、キャッシュを利用する。
log=/var/log/mysql/query.log
その他TIPS
以下の場合はindexが利用できないので、それぞれ別の解決方法を用意する。
offsetを利用したい
offsetを使うと、一度全件取得を行うので、直接利用しない。
offsetを使う場合、インクリメンタルなIDなどの場合は、IDを引数として、そのIDからn件取得という形にする。
<before>
SELECT * FROM account WHERE status = "valid" LIMIT 10 OFFSET 10;
<after>
SELECT * FROM account WHERE status = "valid" AND id > 10 LIMIT 10
rand()は直接使用しない
randで、indexがはられていないカラムを取得しようとすると、indexが利用できない。
面倒だが、indexが効いているカラムを指定してprimary keyを取得し、そこから値を取得する。
<before>
SELECT * FROM account ORDER BY RAND() limit 0,20;
<after>
SELECT * FROM account,
(SELECT id FROM account ORDER BY RAND() LIMIT 0,20) AS random
WHERE account.id = random.id LIMIT 0,20;
負荷テスト
以前利用したものでJMeterがあったが、ユーザシナリオを書くのが面倒で、これはエンジニアの仕事なのか、と思った。
今回はせっかくCucumberを使ってAPIを呼び出せるようにして、全機能のテストもできるようになっていたので、それを利用して負荷テストを行った。
時間がなかったから、というのが最も大きい理由の一つ。
負荷テストの手順
わざわざ書くほどのものではないが、以下のとおり。
- 本番サーバを用意。当たり前だが、本番とまったく同じ性能にしなければ意味がない。
- 負荷テスト用サーバを複数台用意 ※ 本番サーバよりいいものを用意
- 負荷テスト用の全サーバから、本番サーバに向けてcucumberを並列実行
- 本番サーバの負荷状況などは、Amazon Cloud Watchなどを使ってみる。
その他システム側でできそうなこと
今回、データ設計/コードを書く際に、以下のルールを設けた。
- foreign keyを利用しない
- JOIN句を利用しない
これはDBを垂直分割する際に、移行をスムーズにするため。
エラー対策
エラー対策はテストコードをしっかり書くこと。
またエラーが発生し、それが致命的なエラーだった場合は、
指定のメールリストにメールが届くようにした。
メールリストには関係者全員が入っており、エンジニア以外にも送信する。
メールの内容
調査時に、必要最低限となる情報を入れている。
エラーが発生しました。
Server : https://api.sampleerror.com
Endpoint : /v1/sample/endpoint
IP : xxx.xxx.xxx.xxx
Time: 2016-07-02 13:03:21
Msg : Call to a member function set() on a non-object
File: /home/ec2-user/projects/sample-api/src/Account.php
Line: 114
今後は、致命的ではないエラーに対しても、エラーの種類ごとに分類して、1日に1回まとめてメールを送付するようにしたい。