Python3 Bottleフレームワーク入門(その7)- マルチスレッドWEBサーバ(Paste,Rocket)
2017/08/25
2024/03/10
タグ: Bottle, Python, フレームワーク, マルチスレッド
今回の記事ではBottleのWEBサーバについて解説していきたいと思います。Bottleの標準で用意されているWEBサーバはシングルスレッドのWSGIサーバ(wsgirefサーバ)です。Bottleに限らずほとんどの様々な言語のフレームワークはテスト用にシングルスレッドのWEBサーバが用意されています。あくまでも開発中に作ったものを確認するために用意されたものです。しかし、マルチスレッドでもう少し挙動を確認したい場合もあるはずです。やはりシングルとマルチではプログラムの作り方が根本的に違います。また、pythonで作ったWEBサーバに関してセキュリティ攻撃や機能的に心もとないと思うならリバースプロキシーWEBサーバでApache連携すれば良いですが、その場合はPythonコンテンツ側はマルチスレッドである必要があります。故にマルチスレッドでの動作ができる内部連携するWSGI WEBサーバをご紹介したいと思います。
マルチスレッドpasteモジュール
Bottleをマルチスレッドで動作させるモジュールは色々ありますが古くからあり使いやすいのがpasteモジュールです。今回はpasteモジュールのインプリメントを見ていきましょう。
※2023/03/8 にPasteWebサーバの設定方法を刷新しました。ログ出力も対応しています。画面とファィルに出力されます。後半のRocketServerは既に古くてpython3.6以前でないと動作しないと思います。
pasteモジュールの導入
まず最初にpasteモジュールを導入します。
1 2 |
$ pip3 install paste $ pip3 install mako |
簡単なマルチスレッド起動のコーディングを書く
makoテンプレートエンジンを使っていますので、テンプレートはPython3 Bottleフレームワーク入門(その1)で紹介しているものをそのまま使ってください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import sys import logging from bottle import mako_view as view from bottle import Bottle, route, PasteServer, request app = Bottle() # ログの設定 logger = logging.getLogger('access_logger') logger.setLevel(logging.INFO) # ログフォーマットの設定 formatter = logging.Formatter('%(asctime)s - %(levelname)s - Accessed: %(url)s - User-Agent: %(user_agent)s') # ログファイルへのハンドラを追加 file_handler = logging.FileHandler('access.log') file_handler.setLevel(logging.INFO) file_handler.setFormatter(formatter) logger.addHandler(file_handler) # アクセスログを出力するためのデコレータ関数 def log_access(func): def wrapper(*args, **kwargs): logger.info('', extra={'url': request.url, 'user_agent': request.headers.get('User-Agent')}) return func(*args, **kwargs) return wrapper @app.route('/hello/:names') @view('hello') @log_access def index(names): return {'name': names} if __name__ == "__main__": app.run(server=PasteServer, host="0.0.0.0", port='8000', daemon_threads=True, threadpool_workers=10,use_threadpool=True) |
負荷テスト
nperfを使って負荷テストをしてみましょう。-cが並行スレッド数で-nがテストの回数です。
◯ nperfのインストール
1 |
$ sudo npm install -g http-perf |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
$ nperf -c10 -n 60 http://localhost:8080/hello/tanaka [status] response# /request_id time: client time (ms) (server time (ms)) [200] 1 /0 time: 59 (-1) [200] 2 /1 time: 10 (-1) [200] 3 /2 time: 6 (-1) [200] 4 /3 time: 5 (-1) [200] 5 /4 time: 5 (-1) [200] 6 /5 time: 5 (-1) [200] 7 /6 time: 5 (-1) [200] 8 /7 time: 5 (-1) [200] 9 /8 time: 5 (-1) [200] 10 /9 time: 5 (-1) [200] 11 /10 time: 5 (-1) [200] 12 /11 time: 5 (-1) [200] 13 /12 time: 5 (-1) [200] 14 /13 time: 6 (-1) [200] 15 /14 time: 5 (-1) [200] 16 /15 time: 5 (-1) [200] 17 /16 time: 5 (-1) [200] 18 /17 time: 5 (-1) [200] 19 /18 time: 5 (-1) [200] 20 /19 time: 5 (-1) [200] 21 /20 time: 5 (-1) [200] 22 /21 time: 5 (-1) [200] 23 /22 time: 5 (-1) [200] 24 /23 time: 5 (-1) [200] 25 /24 time: 5 (-1) [200] 26 /25 time: 5 (-1) [200] 27 /26 time: 5 (-1) [200] 28 /27 time: 5 (-1) [200] 29 /28 time: 6 (-1) [200] 30 /29 time: 5 (-1) [200] 31 /30 time: 5 (-1) [200] 32 /31 time: 6 (-1) [200] 33 /32 time: 5 (-1) [200] 34 /33 time: 5 (-1) [200] 35 /34 time: 6 (-1) [200] 36 /35 time: 5 (-1) [200] 37 /36 time: 5 (-1) [200] 38 /37 time: 5 (-1) [200] 39 /38 time: 6 (-1) [200] 40 /39 time: 10 (-1) [200] 41 /40 time: 5 (-1) [200] 42 /41 time: 5 (-1) [200] 43 /42 time: 5 (-1) [200] 44 /43 time: 5 (-1) [200] 45 /44 time: 5 (-1) [200] 46 /45 time: 5 (-1) [200] 47 /46 time: 5 (-1) [200] 48 /47 time: 5 (-1) [200] 49 /48 time: 5 (-1) [200] 50 /49 time: 5 (-1) [200] 51 /50 time: 5 (-1) [200] 52 /51 time: 5 (-1) [200] 53 /52 time: 5 (-1) [200] 54 /53 time: 5 (-1) [200] 55 /54 time: 5 (-1) [200] 56 /55 time: 5 (-1) [200] 57 /56 time: 5 (-1) [200] 58 /57 time: 5 (-1) [200] 59 /58 time: 5 (-1) [200] 60 /59 time: 6 (-1) stats: { statuses: { '200': 60 }, min: 5, max: 59, avg: 6.183333333333334, count: 60, rate: 143.88489208633092, start: 1532730411094, total_time: 417 } |
Rocketを使ったマルチスレッド対応
最近使った感じではこちらのRocketの方がpasteより良い感じ。pasteはPython2.7からPython3.xの対応が中途半端でエラーを吐くことがあるのでメンテがいまいち。Rocketも最新バージョンがバグがありエラーとなるのでpipから導入したものは起動エラーが生じ動作に至りませんでした。まともに動くForkしたバージョン(1.2.4)で利用すると良いです。
Rocketサーバのインストールとロギング機能の設置
1 2 3 4 |
$ git clone https://github.com/errbotio/rocket.git $ cd rocket $ sudo python3 setup.py install $ sudo pip3 install wsgi-request-logger |
Rocketを使ったマルチスレッドサーバ(server.py)のソースコード
ログ出力の機能を加えました。Apacheのログ形式で実行ディレクトリ上のrocket.logに吐き出されます。
ちなみにport番号の後にサーバ証明書、プライベート鍵を記述するとHTTPS対応になりますので是非トライしてみてください。
Apacheサーバでリバースプロキシー連携するならSSLはApache側に持たせたほうが絶対に良いです。理由は細かいSSLオプション関連の制御ができるからです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from rocket import Rocket from bottle import Bottle,template from requestlogger import WSGILogger, ApacheFormatter from logging.handlers import TimedRotatingFileHandler app=Bottle() @app.route('/hello/< uname >') def index(uname): return template('<b>Hello {{ var_name }}</b>!', var_name=uname) web_handlers = [ TimedRotatingFileHandler('rocket.log', 'd', 7) , ] app_logfix = WSGILogger(app, web_handlers, ApacheFormatter()) app_info = dict(wsgi_app=app_logfix,future=True) #server = Rocket(interfaces=[('0.0.0.0', 9043,'/etc/pki/org/serv.pem','/etc/pki/org/priv.pem')],method='wsgi',app_info=app_info,min_threads=3,max_threads=13,timeout=300) server = Rocket(interfaces=[('0.0.0.0', 8080)],method='wsgi',app_info=app_info,min_threads=3,max_threads=13,timeout=300) server.start() |
サーバの実行
1 |
$ python3 server.py |
- Python Bottle Framework入門 全13回
- 1.基礎編サーバ起動
- 2.リクエストメソッド
- 3.ORM Peewee (MySQL)
- 4.ORM Peewee CRUD
- 5.Cookie And Session
- 6.Abort and Redirect
- 7.マルチスレッドWEBサーバ
- 8.デーモン化
- 9.Json
- 10.WSGI on SSL
- 11.Apache連携起動(外部WSGI) SSL接続
- 12.Apache連携起動(ReverseProxy)SSL接続
- 13.hprox連携起動(ReverseProxy)SSL接続&HTTP2対応