Released Kobin v0.1.0: Web Framework for Python3.
少し開発が停滞気味だったのですが、最近多くのアップデートを加えたので、v0.1.0をリリースしました。それに伴い、WSGIミドルウェアやWSGICLIの開発、ドキュメントの整備を行ったので紹介。
Kobin
Kobinの説明や使い方は、ドキュメンテーションを書いたのでここでは省略します。
また実際に自分でKobinとSQLAlchemy、Angular2などを使って、サンプルのアプリケーションを書いているので、とりあえず雰囲気を知りたい方はそちらをご覧ください。
WSGICLI
このツールはKobinに限らず、BottleやFlaskといった他のフレームワークでも利用出来ます。 Kobinはまだまだ開発途中な点もありますが、WSGICLIはもう十分に便利なツールかと思っているので、ぜひ使ってみてください。
簡単な使い方の例をいくつか紹介します。
ISUCONでPython実装をプロファイリング
このツールはLine Profiler連携とLive Reload機能のおかげで、ISUCONの問題を解くときにかなり便利なツールになったかなと個人的に思っています。
$ wsgicli run app.py app -h 0.0.0.0 -p 9000 --reload --lineprof
これを起動しておくと、手元のマシンでコードを変更・git pushして、git pullするだけで反映されます。 今はlineprofilerのみサポートしていますが、vmprofも追加予定です。
run
コマンドには他にも、静的ファイルの配信機能や、WSGIの仕様(PEP3333)に準拠しているかチェックするミドルウェアやフレームワークの開発に便利な機能があります。
wsgicli shellコマンド
django-extensionsのshell_plusをSQLAlchemyやpeeweeでも使用出来るようにしたような機能です。 Pythonの対話シェルを起動して、peeweeやSQLAlchemyのModelクラスをインポートしてゴニョゴニョすることがありますが、wsgicliのshellコマンドを使用すると起動時に自動でモデル定義を探索してimportしてくれます。
$ wsgicli shell app.py -i ipython
- Python(Plain)
- iPython
- bpython
- ptpython
- ptipython
その他
その他にもlinebotのサンプルやWSGIのミドルウェアを作っているので、ぜひご覧ください。
WebフレームワークといくつかのWSGIミドルウェア、wsgicli、サンプルアプリ、日英ドキュメントを全て1人で用意するのは思っていたよりも体力と時間が必要みたいです。 tomcriestieさんとか見てるとすごい勢いで開発していて焦りますね。 時間は思っていたよりも少ないみたいなので、2017年はもっとスピードを上げて沢山のコード書いていきたい。
- 作者: Mark Summerfield,斎藤康毅
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/12/01
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
Webフロント周りのパフォーマンスを計測・チューニングする
Github Pagesで公開しているポートフォリオサイトがそんなに色んな情報載せてる訳でもないのですが、遅くてあまり気持ちのいいものではないので、最適化していきます。
結論から書くと、2.87sぐらいかかっていた読み込みが1.57sぐらいになりました。 まだまだ細かいチューニングの余地はありますが、コストのわりに効果の大きそうなところを探って対処してみました。
- Chrome Developer Tools, Page Speed Insightsとかで原因を探る
- JavaScriptのminify
- 大きい画像はプログレッシブ形式のJPEGに変換
- ImageOptim
- CloudFrontでCache, 配信, HTTP/2対応
タイトルにはチューニングと書きましたが、基本的な最適化をちゃんとすればちゃんと表示速度が改善された感じです。
計測
まずは現状を確認
大きなヘッダ画像があるのでそこの読み込みが気になりますね。
Chrome Dev Tools
読み込み完了まで、2.87s。 グラフを見ると、bundle.js(このポートフォリオでは、Angular2 + TypeScriptを使ってます)とヘッダ画像が大きく特に読み込みに時間がかかっていることがわかります。
Page Speed Insights
Web Site: https://developers.google.com/speed/pagespeed/insights/?hl=ja
- Mobile: Speed 56/100
- Mobile: User Experience 97/100
- PC: Speed 76/100
モバイルの表示が特に遅いですね。 Google先生からのアドバイスは、
- JavaScriptの圧縮
- スクロールせずに見えるコンテンツのレンダリングをブロックしている JavaScript/CSS を排除する
- ブラウザキャッシュを活用する
Web Pagetest
Web Site: https://www.webpagetest.org/
海外とかいろんな環境でのサイトのパフォーマンスをチェックできるらしい。 このページからのアドバイスは、
- CDNを使う
- 画像を圧縮する
- 静的コンテンツをキャッシュする
最適化
JSのminify
まずは手軽で効果のありそうなところから。
webpackのhelpを見たら --optimize-minimize
というオプションがあったので有効に。
webpack --optimize-minimize ./src/ts/index.js --output-filename ./public/js/bundle.js
- bundle.jsのファイルサイズ: 592kB > 277kB
- bundlejsの読み込み時間: 1.31s > 753ms
- Page Speed Insights Mobile Score: 56点 > 59点
- Page Speed Insights PC Score: 76点 > 87点
580msぐらい速くなりましたね。
ヘッダ画像の改善
905KBもあるのでこれも改善できそうですね。 JPEGには、左上から画像をパラっと読み込んでくれるベースライン形式と最初はモザイクが出るプログレッシブ形式があるみたいです。プログレッシブ形式のほうがユーザ体験は良いと言われているそうです。
最初のGIF画像を見てもらうと分かるのですが明らかにベースライン形式の挙動を示していますね。 rdjpgcomというツールで確認、imagemagickで変換出来るみたいです。
$ rdjpgcom -verbose img/eyecatches/autumn_leaves.jpg JPEG image is 1500w * 1000h, 3 color components, 8 bits per sample JPEG process: Baseline $ convert img/eyecatches/autumn_leaves.jpg -interlace JPEG progressive.jpg $ rdjpgcom -verbose progressive.jpg JPEG image is 1500w * 1000h, 3 color components, 8 bits per sample
うーん、確かにモザイクが出るプログレッシブ形式ですね。ただユーザ体験がいいかというとなんとも言えない感じです。 よくなったのかイマイチ実感できないので、画像サイズを落としてみましょう。 画像サイズが小さくなって読み込みが早くなれば、わかりやすく快適になってくれそうです。
Optimで画像サイズを小さくする
Web Site: ImageOptim — better Save for Web
JPEG Miniは高いので、Optimを使って画像サイズを減らしてみます。 メタ情報やカラープロファイルを削除するらしいです。
- 画像サイズ: 905KB > 817KB
まだ結構でかいですね。 この画像、1500x1000だったのですが、横幅1280pxぐらいが多いみたいなので思い切って減らしてみます。
- 画像の読み込み時間: 1.47s > 988 ms
- 画像のサイズ: 905KB > 626 KB
- Page Speed Insights Mobile Score: 59点 > 63 点
- Page Speed Insights PC Score: 変化なし
まだ少し重い気もしますが、横幅いっぱいに広げる画像なのである程度は仕方がないかもしれません。 別のところに手を付けます。
CloudFrontでCache, HTTP/2対応
Web Pagetestの結果では、海外からアクセスした際にレイテンシが大きいのか、CDNを使えとのメッセージがありました。 HTTP2のストリーム多重化で速くなることも期待して、AWS CloudFrontをかませてみます。 (余談: HTTP/2を使うかHTTP/1.1を使うか、どうやって判断するのか調べてみたんですが、TLSのネゴシエーションの際にHTTP/2が使えるか確認してくれるみたいですね)
今回は検証目的なので、 http://c-bata.link は一旦そのままでこのURLをoriginにしてCloudFrontをかませます。 Webフォントをhttpで読み込んでいる部分があるのでそこを修正してCloudFrontのCacheをInvalidate後、何度かリロードする(Cacheさせる)と全体の読み込み時間: 1.57sまで上がりました。
グラフを見る限り、HTTP2のストリーム多重化がかなり効いているような気がしますね。
おわりに
最終的な表示はこのようになりました。 CloudFront対応はドメインと証明書の問題で、まだ http://c-bata.link のほうには反映していませんが、CloudFront経由後は読み込み速度が約半分の1.5s程度になり、GIFアニメーションの通り体感的なスピードも改善されたようです。
ハイパフォーマンスWebサイト ―高速サイトを実現する14のルール
- 作者: Steve Souders,スティーブサウダーズ,武舎広幸,福地太郎,武舎るみ
- 出版社/メーカー: オライリージャパン
- 発売日: 2008/04/11
- メディア: 大型本
- 購入: 32人 クリック: 676回
- この商品を含むブログ (127件) を見る
- 作者: 小出淳子,黒澤剛志,牧大輔,横江亮佑,山口貴也,尾藤正人,佐藤琢哉,中橋研太郎,田中慎司,小西裕介,伊藤直也,稲富駿,前島真一,長野雅広,山際康貴,のざきひろふみ,うらがみ,岡林大,遠藤雅伸,ひげぽん,海野弘成,はまちや2,竹原,大場寧子,大場光一郎,野々下裕子,WEB+DB PRESS編集部
- 出版社/メーカー: 技術評論社
- 発売日: 2016/10/22
- メディア: 大型本
- この商品を含むブログを見る
h2load, apache benchによる負荷テスト
前回の記事で触れたApache JMeterやGatlingとは違って、今回試したのはシナリオとかもなくHTTPのリクエストを大量に投げて、スループットやレイテンシを計測してみたい時に便利なツール。
実際に試したのは次の2つ。他にも、wrkやvmbench、siegeが気になったので試したらまた追記。
h2load
- Installation: HTTP/2 に対応するベンチマークツール h2load を Build するメモ | ぶっちろぐ
- http/2に対応
Command:
$ h2load -n 1000 -c 200 --h1 http://example.com/test.jpg
-n
: 総リクエスト数-c
: 並行リクエスト数--h1
: HTTP/1.1を使用
ab: apache bench
- Installation: Apacheを入れたら、一緒に入ります (
# yum install httpd
)。 -t
オプションがあって、時間指定ができる。
Command:
基本的には、h2loadと同じような
$ ab -n 1000 -c 200 http://example.com/test.jpg
並列 200で10秒間リクエストを送る
$ ab -t 10 -c 200 http://example.com/test.jpg
Sanicのベンチマークを試す
refs: GitHub - channelcat/sanic: Python 3.5+ web server that's written to go fast
Sanicというuvloopを使ったPythonのWebサーバ・フレームワークは、wrkを使ってベンチマークをとっています。試しにh2loadやabでも大体同じようなスループット、レイテンシになるのか試してみた。 全部試すのは大変なので、Sanicの負荷計測をしてみます。
環境は、sanicのREADMEに合わせて
$ sudo apt-get update && sudo apt-get upgrade $ sudo apt-get install python3-venv $ python3 -m venv venv $ source venv/bin/activate (venv) $ which pip /home/ubuntu/venv/bin/pip
tornadoの負荷テスト
用意したtornadoのコードはこちらです。
import tornado.ioloop import tornado.web import json class MainHandler(tornado.web.RequestHandler): def get(self): self.write(json.dumps({"hello": "world"})) def make_app(): return tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": app = make_app() app.listen(8080) tornado.ioloop.IOLoop.current().start()
h2loadの結果
$ h2load -n 5000 -c 100 --h1 http://ec2-hoge.ap-northeast-1.compute.amazonaws.c om/ starting benchmark... spawning thread #0: 100 total client(s). 5000 total requests Application protocol: http/1.1 progress: 10% done progress: 20% done progress: 30% done progress: 40% done progress: 50% done progress: 60% done progress: 70% done progress: 80% done progress: 90% done progress: 100% done finished in 2.52s, 1981.37 req/s, 412.14KB/s requests: 5000 total, 5000 started, 5000 done, 5000 succeeded, 0 failed, 0 errored, 0 timeout status codes: 5000 2xx, 0 3xx, 0 4xx, 0 5xx traffic: 1.02MB (1065000) total, 761.72KB (780000) headers (space savings 0.00%), 87.89KB (90000) data min max mean sd +/- sd time for request: 35.61ms 89.74ms 49.25ms 3.17ms 94.78% time for connect: 34.44ms 36.57ms 35.34ms 433us 65.00% time to 1st byte: 70.52ms 125.97ms 97.61ms 15.36ms 59.00% req/s : 19.83 20.20 20.02 0.11 56.00%
スループットは、 1981.37 req/s
で、1st byteの時間、つまりレイテンシやレスポンスタイムと呼ばれる応答があるまでの時間に関しては、こちらは平均 97.61ms
でした。
sanicのページに書かれていたのは、スループットが 2138 req/s
、レイテンシが 46.66ms
なのでスループットはわりと誤差の範囲に収まっている気がしますが、レイテンシは2倍近くの時間差があります。
試しにapache benchでも測ってみました。
apache benchの結果
$ ab -n 5000 -c 100 http://ec2-hoge.ap-northeast-1.compute.amazonaws.com/ This is ApacheBench, Version 2.3 <$Revision: 1430300 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking ec2-52-193-139-58.ap-northeast-1.compute.amazonaws.com (be patient) Completed 500 requests Completed 1000 requests Completed 1500 requests Completed 2000 requests Completed 2500 requests Completed 3000 requests Completed 3500 requests Completed 4000 requests Completed 4500 requests Completed 5000 requests Finished 5000 requests Server Software: TornadoServer/4.4.2 Server Hostname: ec2-52-193-139-58.ap-northeast-1.compute.amazonaws.com Server Port: 80 Document Path: / Document Length: 18 bytes Concurrency Level: 100 Time taken for tests: 3.788 seconds Complete requests: 5000 Failed requests: 0 Write errors: 0 Total transferred: 1065000 bytes HTML transferred: 90000 bytes Requests per second: 1320.01 [#/sec] (mean) Time per request: 75.757 [ms] (mean) Time per request: 0.758 [ms] (mean, across all concurrent requests) Transfer rate: 274.57 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 34 35 0.5 35 41 Processing: 35 38 4.6 38 93 Waiting: 35 38 4.6 38 93 Total: 69 74 4.7 73 129 Percentage of the requests served within a certain time (ms) 50% 73 66% 73 75% 74 80% 74 90% 75 95% 77 98% 80 99% 100 100% 129 (longest request)
スループットが、 1320.01 req/s
と少し小さいようです。
レイテンシの方(Time per request)は平均 75.757ms
とh2loadの結果と近い値がでました。
sanicのREADMEに書いてあるbench scoreと比べると、どちらもレイテンシの差が特に気になります。 よくよく考えると負荷をGCEのインスタンスからかけていたのですが、ネットワークの影響が大きいのかもしれません。 並列100個のリクエストをTornadoに送ったときのhtopを見る限り、MediumインスタンスのCPU・メモリリソースではまだまだ余裕があるので、 Mediumインスタンスの中から負荷をかけて、ネットワークの影響を減らしてみます。
Localhostで試してみる
$ h2load -n 5000 -c 100 localhost:8080 invalid URI: localhost:8080 ubuntu@ip-172-31-22-107:~$ h2load -n 5000 -c 100 --h1 http://localhost:8080 starting benchmark... spawning thread #0: 100 total client(s). 5000 total requests Application protocol: http/1.1 progress: 10% done progress: 20% done progress: 30% done progress: 40% done progress: 50% done progress: 60% done progress: 70% done progress: 80% done progress: 90% done progress: 100% done finished in 2.38s, 2099.97 req/s, 436.81KB/s requests: 5000 total, 5000 started, 5000 done, 5000 succeeded, 0 failed, 0 errored, 0 timeout status codes: 5000 2xx, 0 3xx, 0 4xx, 0 5xx traffic: 1.02MB (1065000) total, 761.72KB (780000) headers (space savings 0.00%), 87.89KB (90000) data min max mean sd +/- sd time for request: 1.27ms 138.37ms 46.64ms 6.02ms 97.68% time for connect: 643us 1.53ms 995us 227us 61.00% time to 1st byte: 2.64ms 139.75ms 35.19ms 33.23ms 92.00% req/s : 21.00 21.66 21.43 0.16 76.00%
スループットが2099.97 req/s、2,138msとくらべてかなり近い値が出ました。 ただ、1st byteまでの時間が2.64msとsanicのREADMEに比べかなり速い結果になってしまいましたね... 一応apache benchも試してみます。
$ ab -n 5000 -c 100 http://localhost:8080/ This is ApacheBench, Version 2.3 <$Revision: 1706008 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient) Completed 500 requests Completed 1000 requests Completed 1500 requests Completed 2000 requests Completed 2500 requests Completed 3000 requests Completed 3500 requests Completed 4000 requests Completed 4500 requests Completed 5000 requests Finished 5000 requests Server Software: TornadoServer/4.4.2 Server Hostname: localhost Server Port: 8080 Document Path: / Document Length: 18 bytes Concurrency Level: 100 Time taken for tests: 3.250 seconds Complete requests: 5000 Failed requests: 0 Total transferred: 1065000 bytes HTML transferred: 90000 bytes Requests per second: 1538.36 [#/sec] (mean) Time per request: 65.004 [ms] (mean) Time per request: 0.650 [ms] (mean, across all concurrent requests) Transfer rate: 319.99 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.2 0 2 Processing: 1 64 8.6 66 168 Waiting: 1 64 8.6 66 168 Total: 2 64 8.5 66 169 Percentage of the requests served within a certain time (ms) 50% 66 66% 67 75% 67 80% 67 90% 70 95% 73 98% 74 99% 75 100% 169 (longest request)
apache benchの方は、先ほどと同じくh2loadの結果に比べて少しかかった時間が長く、スループットも悪い結果となりました。 リクエストの総数を10倍の50000個にしても、レスポンスタイムやスループットに違いは見られませんでした。 ベンチマーカー側の性能の差でこれぐらいの違いが出てしまうのは仕方ないのかもしれない。
もうちょっとわかったことがあれば追記。 ここがおかしいとかあればコメントもお待ちしてます。
References
今回は試さなかったけど、siegeやvmbench、wrkが使われているのを見かけた。
- 作者: 小出淳子,黒澤剛志,牧大輔,横江亮佑,山口貴也,尾藤正人,佐藤琢哉,中橋研太郎,田中慎司,小西裕介,伊藤直也,稲富駿,前島真一,長野雅広,山際康貴,のざきひろふみ,うらがみ,岡林大,遠藤雅伸,ひげぽん,海野弘成,はまちや2,竹原,大場寧子,大場光一郎,野々下裕子,WEB+DB PRESS編集部
- 出版社/メーカー: 技術評論社
- 発売日: 2016/10/22
- メディア: 大型本
- この商品を含むブログを見る