Apache JMeterメモ
Website: http://jmeter.apache.org/
Apache JMeterを教えてもらったので、後で見返せるようにメモ。
Apache Software Foundationで開発されているオープンソースのJavaアプリケーション Webサーバーに対して複数のHTTPリクエストを送信し負荷をかけたり、JDBCを経由してデータベースサーバーに負荷をかけたりできる
基本的な流れ
起動
最新のzipバイナリをダウンロード・解凍。
$ cd apache-jmeter-3.0/bin $ java -jar ApacheJMeter.jar
スレッドグループ
リクエストを送るためのもの。 よく使うのはHTTP SamplerとJDBC Sampler.
- Host:
192.168.45.10
- Port: 80 (80の場合は省略可)
- Path:
/pukiwiki/
リスナー
負荷はもうかけられるが、結果を見るための設定がない。それがリスナー。
「結果をツリーで表示」と「結果を表で表示」をクリック。
- 結果をツリーで表示からは、リクエストや応答データが見れる
- 結果を表で表示は、ズラッと全部見たい時に便利
- Connect Time: 接続時間
- スレッド数1でループ回数を5とかにすると、keep-aliveが有効なおかげで2回目以降のリクエストのconnect timeが0になるのが確認できる。
- Connect Time: 接続時間
リスナーは何回も実行すると溜まっていくので、「ほうき」のアイコンの「消去」で掃除できる。
実行
「開始」ボタンを押したら開始出来る。
シナリオキャプチャ
ブラウザで操作を記録させて、シナリオを簡単に作ることができる。
HTTPプロキシサーバを追加
- Portは使っていない番号に設定 (ここでは8999)
- 除外するパターンを
.*.css.php.*
と最後にも.*
を付けているのは、クエリパラメータも含めるため
HTTPプロキシの設定
Chromeの「設定」>「詳細設定」>「プロキシ設定の変更」から、上で設定したポート番号を指定。
開始
「HTTPプロキシサーバ」>「開始」を押したら(ここで鍵がどうのこうのってメッセージが出たけどとりあえずOKで進めてみた)、Chromeを開いて検証したいようにブラウザで操作。
終わったら「HTTPプロキシサーバ」>「停止」
これで終わり。緑の「開始」ボタンをクリックすると実行してくれる。
その他
ユーザ定義変数
設定しておくと、次に127.0.0.1とかが出てきた時に、自動で ${PUKIWIKI_HOST}
とかを埋め込んでくれる。
あとでこの変数のvalueを変えるだけで済んで便利。
HTTPリクエストの詳細設定
「Advanced」>「全てのイメージとアプレットを繰り返してダウンロードする」を選択すると、そこのページのCSSや画像も全部ダウンロードしてくれる。
スレッドグループの設定
スレッド数やループ回数以外にもいくつかオプションがある。 例えば、60秒間負荷をかけたい場合は、ループ回数を無限、持続時間を60にすればいい。
MySQL(MariaDB)の計測
Setup
CentOS7にMariaDBを入れる
$ yum update $ yum install mariadb mariadb-server $ systemctl start mariadb $ systemctl enable mariadb
参考: 【jmeter】【MySQL】MySQLからデータを取得し、変数に格納する - たんたんめん日記
MySQL :: Download Connector/J から zipパッケージをダウンロードして展開、 mysql-connector-java-5.1.40-bin.jar
を apache-jmeter-3.0/lib
に入れる。
1つのRDBに負荷をかける
うまくいくとこのようになる。権限周りでちょっとはまりましたが、「結果をツリーで表示」からレスポンスをみて修正。
複数のDBに対してロードバランス
レプリケーションをしていて、マスタや複数のスレーブに対してリクエストを分散させたい場合、
jdbc:mysql:loadbalance://<FQDN or IP address>:<port>,<FQDN or IP address>:<port>/<DB name>
JMeter Server
複数台のPCからリクエストを投げる。 下の記事の通りやったら問題なく動いた。 zipパッケージの中に jmeter-serverとかも最初から入ってて、j
複数台のJMeterサーバで負荷試験を行う方法 | Check!Site
テスト計画の保存
ファイルメニューからテスト計画を保存できる。 XML形式で保存されているので、構造もそれほど複雑ではない。 プログラムとかExcelマクロで生成できそう。
キャプチャ
JMeter Proxy Serverを経由することで、実際のユーザ−の行動からリクエストをキャプチャーしたりできる。
CUI
試してないけど、 *.jmx
で保存して bin/jmeter.bat
から実行出来るらしい。
自動化するならこっちのほうが大事そう。
おわりに
気が向いたら今度、グラフとかレポートがリッチらしいGatlingも使ってみたい。
- 作者: Brendan Gregg,西脇靖紘,長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/02/22
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
Electronでニコ動のコメントみたいにTweetを表示するアプリつくった
知り合いのエンジニア(rayさん)がGTK3で作っていた nsd というのがあるんですが、結構いい感じなのでMacでも動くものが欲しかった。 Electronで似たようなことが出来ないか調べてみると、GTK3とかに比べて簡単にできそうだったので作ってみた。
ポイントは:
- 常に最前面にtweetが表示される
- マウスのイベントが後ろに透過する
- コーディングだったり他の作業が問題なく出来る
今度、カンファレンスや勉強会で話す時にハッシュタグ表示してみます。
ロゴも描いてみた
マウスのイベントの透過について
rayさんのnsdとかの場合、tweetのテキストを囲うだけのとても小さくて透明なウィンドウを用意していたらしい。こうすることで文字以外のところをクリックすると後ろにマウスのクリックイベントとかが通る仕組み。 透明なウィンドウを作れるGUIツールキットはたくさんあるみたいなのですが、Electronは簡単にマウスのイベントを透過できた。
ハマったところとか参考にした記事
- Electronは半年以上前に、Hello Worldをしただけだったので、今回もQuickStartからやり直した。
- 透明なウィンドウのままで、開発者ツールを使わないとデバッグがかなり難しかった。
- 開発者ツールを開く処理だったりを、もうちょっとカジュアルに環境変数とかで変更できるようにしておくとスムーズだったかも
- Electronでデスクトップいっぱいに雪を降らせるアプリを作る - Qiita
- これを見つけたので今回やってみることにしました。感謝!透過の設定やマウスを通す設定を真似させていただきました。
- tweetのデータをフロントに送る方法で悩んでいたら、 electron/ipc-renderer.md at master · electron/electron · GitHub というのをみつけた。
- とても簡単にIPCができる
- canvasの文字がぼやけて困っていたのですが、canvas自体の解像度(幅と高さ)を調整しないといけなかったみたいです。
- devicePixelRatioというものを教えていただけました。ありがとうございます!
@c_bata_ devicePixelRatio 見てないからじゃないでしょうか? canvas は retina ディスプレイとか考慮してくれないので,自前で倍率調整する必要があると思います.
— ドッグ (@Linda_pp) October 11, 2016
あとやりたいこと
自分が使うには、最低限欲しい機能が全部揃ったのでとりあえず満足。 設定のダイアログとかを追加して、そこから認証してAccess Tokenとったりハッシュタグ指定できるようにしたい。 Issue にあげてるので、おもしろそうと思った方はぜひPRお願いします。
追記
とりあえずMacの右上のバーに常駐させて、色々と設定画面とかも用意中。
PIPEによるプロセス間通信とselect, poll, epollの話
エキスパートPythonプログラミング 改訂2版 (アスキードワンゴ)
- 作者: Michal Jaworski,TarekZiade,稲田直哉,芝田将,渋川よしき,清水川貴之,森本哲也
- 出版社/メーカー: ドワンゴ
- 発売日: 2018/02/26
- メディア: Kindle版
- この商品を含むブログを見る
先日、 tokibito 先生(id:nullpobug)と勉強していたpipe, select, poll, epollあたりについてメモ。
os.fork
os.fork
: 子プロセス(child process)をつくれる。
import os a = 0 print(a) os.fork() a += 1 print(a)
子は親の複製。親のデータ、ヒープ、スタックの各空間の複製を取得。 メモリのこれらの部分は共有されないので、実行結果は次のようになる(テキストセグメントは共有される)。
$ python3.5 fork.py 0 1 1
ちなみに親と子、どちらが先に実行され1を出力したのかは、カーネルが使うスケジューリングアルゴリズムに依存。もし親と子で同期をとりたいならプロセス間通信が必要となる。
プロセス記述子(pid)
ちなみに os.fork は、親側には子のPID、子側には0を返します。
- 親や子が自身のPIDを知りたいときは、
os.getpid()
- 子が親のPIDを知りたいときは、
os.getppid()
をよびだします。
import os pid = os.fork() if pid: # Parent Process print('親:自身のPID', os.getpid()) print('親:子のPID', pid) else: # Child Process print('子:自身のPID', os.getpid()) print('子:親のPID', os.getppid())
$ python3.5 fork.py 親:自身のPID 37938 親:子のPID 37939 子:自身のPID 37939 子:親のPID 37938
詳解UNIXプログラミングを読んでいて、たしかにと思ったんですが、この関数は1度だけ呼び出すけど2度戻る。なんか不思議な感じ。
パイプによるプロセス間通信
パイプは os.pipe で作成。ファイル記述子のペア (r, w)
を返し、それぞれ読み出し、書き込み用に使うことができます。これを使ってプロセス間通信を試してみる。
import os def main(): read_fd, write_fd = os.pipe() pid = os.fork() if pid: # Parent process os.close(read_fd) write_pipe = os.fdopen(write_fd, 'w') write_pipe.write('hello') write_pipe.close() else: # Child process os.close(write_fd) read_pipe = os.fdopen(read_fd, 'r') content = read_pipe.read() read_pipe.close() print(content) if __name__ == '__main__': main()
$ python3.5 process-communication.py hello
bytesを読み書きしたい場合は、
def bytes_main(): read_pipe, write_pipe = os.fdopen(read_fd, 'rb'), os.fdopen(write_fd, 'wb') pid = os.fork() if pid: # Parent Process read_pipe.close() write_pipe.write(b'hello') write_pipe.close() else: # Child Process write_pipe.close() content = read_pipe.read(5) print(content) read_pipe.close()
NON BLOCKINGモード
ノンブロッキングで読み書きしてみる
さっきまでは read_pipe.read()
を呼び出すと、値がくるまでブロックしてしまいます。
値が来てない間は、ずっと待ったりせずに他の処理をしたいことがあるかもしれません。
値はあれば返すし、なければ何も無かったよってことを返す。 そうすると、じゃあその間他の処理をしようって判断ができる。
FCNTLというものがあって、File Descriptorにnon blockingモード(os.NON_BLOCKING)を指定出来たりする。
import os import time import fcntl def main(): read_fd, write_fd = os.pipe() pid = os.fork() if pid: print('Parent:', os.getpid()) write_pipe = os.fdopen(write_fd, 'wb') for w in b'Hello': time.sleep(1) write_pipe.write(bytes([w])) write_pipe.flush() write_pipe.close() else: print('Child:', os.getpid()) fcntl.fcntl(read_fd, fcntl.F_SETFL, os.O_NONBLOCK) read_pipe = os.fdopen(read_fd, 'rb') bytes_read = 0 content = b'' while True: read_data = read_pipe.read() if read_data: bytes_read += len(read_data) content +=read_data print(bytes(read_data)) if bytes_read >= 5: break read_pipe.close() print(content) main()
注意点としては、書き込み側も出来るだけ連続したところに書きたいために、Bufferにためています。 writeした時点では、バッファに乗ってるだけなので、ちゃんとフラッシュしてあげないといけません。 (さっきまではcloseする時に、バッファのコピーがフラッシュされていました)
しかし今のプログラムには1つ致命的な問題があります。 これを実行中に、別タブ開いてtop コマンドを叩いてみてください。 CPUの使用率が100%となっています。
select, poll, epoll
selectを触ってみる
import os import time import select def main(): read_fd, write_fd = os.pipe() pid = os.fork() if pid: # Parent Process print('parent', os.getpid()) write_pipe = os.fdopen(write_fd, 'wb') for w in b'Hello': time.sleep(1) write_pipe.write(bytes([w])) write_pipe.flush() write_pipe.close() else: # Child Process print('child', os.getpid()) read_pipe = os.fdopen(read_fd, 'rb') bytes_read = 0 content = b'' exit_flag = False rready, _, _ = select.select([read_fd], [], []) while True: for fd in rready: read_data = read_pipe.read(1) print(read_data) bytes_read += len(read_data) content += read_data if bytes_read >= 5: exit_flag = True break if exit_flag: break read_pipe.close() print(content) main()
実行してみると、
poll, epoll
pollとepollは同じようなインタフェースです。 tokibito先生がepollのサンプルをgistで公開しているので、ここでは省略
違いとか
どれを使うかは結構議論されてるとこなのかな。とりあえずtokibito先生と話してたこと:
- pollとselectはそもそもインタフェースが違う。
- selectの方が簡単そう?
- pollとepollは、Pythonレベルでは同じインタフェース
- pollは毎回ファイルディスクリプタのリストを渡す必要がある。
- つまりPython側で管理している。毎回、kernel領域にメモリコピーをしないといけない。
- epollはregisterの時にファイルディスクリプタのリストを渡すと、kernel側で保持しておいてくれる。
- Man page of EPOLL
- pollと違って、毎回kernel空間にメモリコピーしなくていい
- time.sleep はosのapi呼んでいる。これでもCPU使用率は下げられる
- poll とか使うとイベントが来た時に動ける
- time.sleep だと読む必要がない時に読みに行くし、読まないと行けない時にすぐ読まない
- daemon 系のスクリプト書くときとかもpollとかselect使ったりする
環境によってこれ使えないよとかがある。詳しくはドキュメンテーションに書いてますが、てっとり速く確認するならselectモジュールの中覗けばいい。
Mac OSX
>>> dir(select) ['error', 'kevent', 'kqueue', 'poll', 'select']
>>> dir(select) ['epoll', 'error', 'poll', 'select']
補足
全く本題とは違うんですが、tokibito先生と話していて知ったこととかを、自分用のメモとしてここに書いておきます。
補足1: ファイル共有
forkは、親のオープンしているすべてのファイル記述子を二重化(各記述子について関数dup を読んだのと同じ)します。 親と子はオープンしている各記述子のファイルテーブルエントリを共有。 親と子が同じ記述子へ書き出す場合、親が子を待つ(wait)などの同期をとらないと出力が混ざる。
>>> import os >>> os.fork() 36897 >>> 0 >>> ^C KeyboardInterrupt >>> KeyboardInterrupt >>> >>>
補足2: スレッドについて
当然ですが、スレッドなら名前空間共有されてる。
import threading a = 0 print(a) def main(): global a a += 1 print(a) t1 = threading.Thread(target=main) t2 = threading.Thread(target=main) t1.start() t2.start()
$ python3.5 thread_test.py 0 1 2
補足3: スレッドについて2
OSスレッドとは違って、Erlangのような言語は軽量スレッド・マイクロスレッドと呼ばれるものが実装されていて、OSが提供するスレッド機能の限界を超えた数の並列処理が出来たり、OSのスケジューリングの影響を受けなかったりするらしい。呼ばれるものがあるらしい。
自分の持っているサーバでは、
$ cat /proc/sys/vm/max_map_count
65530
PythonのthreadingモジュールとかはOSが提供するスレッドを生成する。
import threading import time def main(): while True: time.sleep(1) t = threading.Thread(target=main) t.start()
この時にpsでthreadまで表示。
$ ps aux -L | grep python mainte 29700 29700 0.0 2 0.7 206504 7528 pts/3 Sl+ 00:42 0:00 /opt/python-3.5.0/bin/python3 mainte 29700 29762 0.0 2 0.7 206504 7528 pts/3 Sl+ 00:46 0:00 /opt/python-3.5.0/bin/python3
2列目がPID、3列目がPPID。同じプロセスでスレッドが2つ動いているのが確認できる。
Linux におけるスレッド数の上限 | yunabe.jp によると、システム全体でのスレッド数の上限は、下記コマンドで確認できるらしい。 Golangのgoroutineとかもマイクロスレッドなんだけど、 Go言語の低レイテンシGC実現のための取り組み | POSTD とか読むと1プロセスで150万のgoroutineを生成してると聞いて驚いた。
$ ulimit -u # 現在のユーザが起動できるプロセス数の上限( /etc/security/limits.conf で調整可能) 3893 $ cat /proc/sys/kernel/threads-max # システム全体でのスレッド数( /etc/sysctl.conf から調整可能) 7787 $ cat /proc/sys/vm/max_map_count # これを半分にした数を超えてスレッドを作ることはできない 65530
補足4: mod_wsgi について
mod-wsgiはPython VMをつくってる。1プロセス内でメモリ空間(そこにpython vmがあるかんじ)を区切ってる。 これはCからしか叩け無いAPIがあるらしい。 cpythonのプログラムが全部あるわけではなくて、sysモジュールとかそういうのがpython vm内にある。 他は共有してるらしい。
面白いので読んでみるといいとのこと。
補足5: signal
gunicornのソースとかpure pythonで読みやすいし、勉強になるよって話になった。 シグナルとかの扱いを知っておくといいとのこと。
import signal import time import sys def handler(signum, frmae): print('signal: {}'.format(signum)) sys.exit() # ここでexitしなければ動き続ける signal.signal(signal.SIGTERM, handler) # handlerを登録. sigtermが来た時にhandlerを実行 while True: time.sleep(1)
補足6: concurrent.futures
ソース(pure python)を読むと分かるけど、 ProcessPoolExecutorはただのpythonのcollections.queueを用いてじっそうしたQueueオブジェクトでためてる。 そのなかではthread safeにするために(同時にアクセスされても大丈夫)threading.Lock()を呼び出す。
ちなみにロックの実装方法は色々あって、fileをopenができたらlockがとれたみたいなことする実装もある。
補足7: 他への応用
ここではPIPEによるプロセス間通信でselectやpoll, epollを試したんですが、Socket通信とかをNon Blockingにするのもファイルディスクリプタに対して同じようにできる。
References
- 作者: W. Richard Stevens,Stephen A. Rago
- 出版社/メーカー: 翔泳社
- 発売日: 2014/06/05
- メディア: Kindle版
- この商品を含むブログ (7件) を見る