Pythonでしょ!

Pythonでいろいろやってみましょう! ビッグデータ、画像処理、WebSiteなどなど

Tornado vs PHP

Nginx + spawn-fcgi + PHP
vs.
Nginx + Tornado

どっちがいい?
テストしてきました

環境:
Centos 6 64Bit

必要なものインストール:
yum install spawn-fcgi
yum install nginx

spawn-fcgiの確認

$spawn-fcgi
Usage: spawn-fcgi [options] [-- <fcgiapp> [fcgi app arguments]]

spawn-fcgi v1.6.3 (ipv6) - spawns FastCGI processes
...(略)


#起動

$spawn-fcgi -a 127.0.0.1 -p 9000 -P /var/run/phpfcgi.pid -C 1 -f /usr/bin/php-cgi
spawn-fcgi: child spawned successfully: PID: 61836

localhostの9000ポートで実行

#確認

$netstat --tcp -an -p | grep 9000
tcp        0      0 127.0.0.1:9000              0.0.0.0:*                   LISTEN      61836/php-cgi

#nginxの確認

$nginx -v
nginx version: nginx/1.6.2

#nginxの設定
/etc/nginx/conf.d/default.conf

server {
...
}
セクションの中に設定を入れる



設定対象項目:
listen 8081; #自由指定
server_name localhost; #IPでもOK

#PHPに置く場所
root /usr/share/nginx/html;

#URL最後は.phpのURLは127.0.0.1:9000のFCGIにて処理する
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}

Nginx再起動

/etc/init.d/nginx restart

テスト用PHP作成
/usr/share/nginx/html/test.php

<?php
echo "hello world\n"
?>

アクセス
http://192.168.183.138:8081/test.php

結果:
hello world

FCGI+PHP環境設定済み

次は
tornado + nginx設定

1)作成
/usr/share/nginx/html/test.py

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("hello world")

application = tornado.web.Application([
    (r"/tornado/test", MainHandler),
], debug=True)

if __name__ == "__main__":
    application.listen(8889)
    tornado.ioloop.IOLoop.instance().start()

2)起動テスト

python test.py
※バックグラウンド起動   python test.py &>/dev/null &

3)URLテスト
http://192.168.183.138:8889/tornado/test

hello world

4)Nginx proxy設定(リバースプロキシ)
location /tornado/ {
proxy_pass http://127.0.0.1:8889;
}

5)アクセステスト
http://192.168.183.138:8081/tornado/test

hello world

tornado + nginx設定完了


負荷テスト:
httperf を利用する


php:

httperf --server 127.0.0.1 --port 8081 --uri /test.php --rate 100 --num-conn 1000 --num-call 10

tornado:

httperf --server 127.0.0.1 --port 8081 --uri /tornado/test --rate 100 --num-conn 1000 --num-call 10

HelloWorld出力のみの場合:

PHP:
Connection rate: 100.1 conn/s
Request rate: 1000.6 req/s (1.0 ms/req)
Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
Net I/O: 255.0 KB/s (2.1*10^6 bps)

Tornado:
Connection rate: 100.0 conn/s
Request rate: 999.8 req/s
Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
Net I/O: 289.0 KB/s


ここからは重要!!!!!!!!!!!!!
それぞれsleep(0.001)秒なら
実際はDBへのアクセスなどはどうしても待つ時間が発生するので


PHPコード:

<?php
usleep(1000);
echo "hello world\n"
?>

Torandoコード:

import tornado.ioloop
import tornado.web
from tornado.web import gen
import time


class MainHandler(tornado.web.RequestHandler):
    def slp(self, *args, **kwargs):
        time.sleep(0.001)
        return kwargs['callback']()

    @tornado.web.asynchronous
    @gen.coroutine
    def get(self):
        t = yield gen.Task(self.slp)
        self.write("hello world")

application = tornado.web.Application([
    (r"/tornado/test", MainHandler),
], debug=True)

if __name__ == "__main__":
    application.listen(8889)
    tornado.ioloop.IOLoop.instance().start()

結果:

php
Connection rate: 4.9 conn/s
Reply status: 1xx=0 2xx=7806 3xx=0 4xx=0 5xx=1424
test-duration 203.834 s
Request rate: 46.2 req/s (21.7 ms/req)


tornado
Connection rate: 40.7 conn/s
Reply status: 1xx=0 2xx=8849 3xx=0 4xx=0 5xx=118
test-duration 24.600 s
Request rate: 369.3 req/s (2.7 ms/req)


この結果はびっくりしたけど
想定内です
fcgi+php 1プロセス Tornadoは1プロセス

こんな結果出る原因は
tornadoは非同期Webサーバだから


参考まで・・・