vieyahn2017 / iBlog

44 stars 0 forks source link

6.1 Django Tornado Flask web性能对比 #141

Closed vieyahn2017 closed 4 years ago

vieyahn2017 commented 6 years ago

Django Tornado Flask web性能对比

vieyahn2017 commented 4 years ago

1.测试条件

(1)使用apache bench测试,测试命令

单个测试:

./ab -n 10000 http://10.170.103.16:8000

100并发测试:

./ab -n 10000 -c 100 http://10.170.103.16:8000

(2)Django Torando Flask三个Demo均只含有一个简单的请求,返回“ok”

(3)uwsgi&nginx,都只开一个进程

(4)日志不记录/dev/nul

vieyahn2017 commented 4 years ago

2.测试结果

结果为: 请求数/s

服务器 单个请 并发100
Django 255.39 无法完成
Tornado 387 918
Tornado+Nginx(单进程) 317 890
Nginx+uwsgi+Flask(单进程) 342.79 1694
Django+uwsgi+Nginx(单进程) 282 1107
Nginx+uwsgi(4进程)+Gjango 280 2947.90
Nginx+uwsgi(4进程)+Flask 343 4651
vieyahn2017 commented 4 years ago

3.测试工程

见附后

vieyahn2017 commented 4 years ago

4.测试相关命令

启动Django:

python manage.py runserver 0.0.0.0:8000

usgi启动Flask

uwsgi -s /tmp/uwsgi.sock  --chmod-sock=666 -w flaskr:app   -p 4 --logto=/dev/nul

usgi启动Django

uwsgi -x djangochina_socket.xml

启动nginx

./sbin/nginx -c conf/nginx.conf

重启nginx

./sbin/nginx -s reload
vieyahn2017 commented 4 years ago

DjangoDemo

mysite/mysite/settings.py


"""
Django settings for mysite project.

For more information on this file, see
https://docs.djangoproject.com/en/1.7/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.7/ref/settings/
"""

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'z&v(p1fmol17jzae2f3-9e$wbqdiiwxw6^)q=yf)7j#pe0+s6s'
DEBUG = True

TEMPLATE_DEBUG = True

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

ROOT_URLCONF = 'mysite.urls'

WSGI_APPLICATION = 'mysite.wsgi.application'

# Database
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases

# DATABASES = {
#     'default': {
#         'ENGINE': 'django.db.backends.sqlite3',
#         'NAME': os.path.join('C:/Users/x00218443/', 'hello.db'),
#     }
# }

# Internationalization
# https://docs.djangoproject.com/en/1.7/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.7/howto/static-files/

STATIC_URL = '/public/'

mysite/mysite/urls.py

from django.conf.urls import patterns
from mysite.views import *

urlpatterns = patterns('',
                       # Examples:
                       # url(r'^$', 'mysite.views.home', name='home'),
                       # url(r'^blog/', include('blog.urls')),
                       ('^$', hello),
                       ('^hello/$', hello),
                       ('^time/$', current_datetime),
                       (r'^time/plus/(\d{1,2})/$', hours_ahead),
                       )

mysite/mysite/views.py

from django.http import HttpResponse, Http404
import datetime

def hello(request):
    # a = request.session['has_commented']
    return HttpResponse("ok")

def current_datetime(request):
    request.session['has_commented'] = 'hello'
    now = datetime.datetime.now()
    html = "<html><body>It is now %s .</body></html>" % now
    return HttpResponse(html)

def hours_ahead(request, offset):
    try:
        offset = int(offset)
    except ValueError:
        raise Http404()
    dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
    html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)
    return HttpResponse(html)

mysite/mysite/wsgi.py

"""
WSGI config for mysite project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
"""

import os
from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

application = get_wsgi_application()

mysite/django_wsgi.py

#!/usr/bin/env python
# coding: utf-8

import os
import sys

# 将系统的编码设置为UTF8
reload(sys)
sys.setdefaultencoding('utf8')

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

from django.core.handlers.wsgi import WSGIHandler
application = WSGIHandler()

mysite/manage.py

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)

djangochina_socker.xml

<uwsgi>
    <socket>:8000</socket>
    <chdir>/home/forABTest/DjangoDemo/mysite</chdir>
    <module>django_wsgi</module>
    <processes>1</processes>
    <daemonize>uwsgi.log</daemonize>
</uwsgi>
vieyahn2017 commented 4 years ago

FlaskDemo

apps/resource.py

from flask.views import MethodView
from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash

class IndexAPI(MethodView):

    def get(self):
        # print '111'
        # cur = g.db.execute('select title, text from entries order by id desc')
        # entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
        return 'ok'

class ResourceAPI(MethodView):

    def get(self):
        print '111'
        cur = g.db.execute('select title, text from entries order by id desc')
        entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
        return render_template('show_entries.html', entries=entries)

    def post(self):
        if not session.get('logged_in'):
            abort(401)
        g.db.execute('insert into entries (title, text) values (?, ?)',
                     [request.form['title'], request.form['text']])
        g.db.commit()
        flash('New entry was successfully posted')
        return redirect(url_for('show_entries'))

config/DebugConfig.py

# configuration
DEBUG = True
SECRET_KEY = 'development key'
USERNAME = 'admin'
PASSWORD = 'default'

flaskr.py


import sqlite3
from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash
from contextlib import closing
import os
import config.DebugConfig
from flask_restful import reqparse, abort, Api, Resource
import apps.login
import apps.resource

# create our little application :)
rootPath = os.path.split(os.path.realpath(__file__))[0]
print rootPath
app = Flask(__name__)
api = Api(app)

api.add_resource(apps.resource.IndexAPI, '/',endpoint="indexapi")

# default config
app.config.from_object(config.DebugConfig)

if __name__ == '__main__':
    app.run(host='0.0.0.0')
    # http_server = WSGIServer(('', 8888), app)
    # http_server.serve_forever()
vieyahn2017 commented 4 years ago

TornadoDemo

apps/login/login.py

#!/usr/bin/env python
# -*-coding:utf-8-*-

import tornado.web

class LoginHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("ok")

config/conf.py


from tornado.options import define

_CONFIG_FILENAME = "xxx.ini"

def define_options():
    """Define defaults for most custom options"""
    # Log file and config file paths
    # Since we are now using supervisord, we just let it capture the
    #   log from STDERR. The following line is therefore commented out.
    # options.log_file_prefix = "/var/log/cutthroat/cutthroat.log"
    define(
        "conf_file_path",
        default="E:/code/TornadoDemo/config/{}".format(
            _CONFIG_FILENAME),
        help="Path for the JSON configuration file with customized options",
        type="str"
    )

    # Port
    define(
        "port",
        default=8000,
        help=("A list of ports to listen on; each port will be tried"
              " until one can be successfully bound to."),
        type=int
    )

    define(
        "session_timeout_days",
        default=1,
        help=("Cookie expiration time in days; can also be set to `None` "
              "for session cookies, i.e., cookies that expire when "
              "browser window is closed.")
    )

    define(
        "cookie_secret",        
        default="x",
        help=("Set this to an empty string to generate a new cookie secret "
              "each time the server is restarted, or to any string which is "
              "the cookie secret."),
        type=str
    )

server.py

#!/usr/bin/env python
# -*-coding:utf-8-*-

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import time
import os
import apps.login.login
import logging
import config.conf
import ssl
from tornado.options import options

MAX_WAIT_SECONDS_BEFORE_SHUTDOWN = 3

def sig_handler(sig, frame):
    """Handles SIGINT by calling shutdown()"""
    tornado.ioloop.IOLoop.instance().add_callback(shutdown)

def shutdown():
    """Waits MAX_WAIT_SECONDS_BEFORE_SHUTDOWN, then shuts down the server"""
    http_server.stop()
    io_loop = tornado.ioloop.IOLoop.instance()
    deadline = time.time() + MAX_WAIT_SECONDS_BEFORE_SHUTDOWN

    def stop_loop():
        now = time.time()
        if now < deadline and (io_loop._callbacks or io_loop._timeouts):
            io_loop.add_timeout(now + 1, stop_loop)
        else:
            io_loop.stop()

    stop_loop()

if __name__ == "__main__":

    config.conf.define_options()
    try:
        tornado.options.parse_config_file(options.conf_file_path)
    except IOError:
        errmsg = ("{} doesn't exist or couldn't be opened. Using defaults."
                  .format(options.conf_file_path))
        logging.error(errmsg)

    app = tornado.web.Application(
        handlers=[
            (r"^/$", apps.login.login.LoginHandler),
        ],
        static_path=os.path.join(os.path.dirname(__file__), "public"),
        static_url_prefix="/test/",
        login_url="/login.html",
        cookie_secret=options.cookie_secret,
        debug=True
    )  

    http_server = tornado.httpserver.HTTPServer(app)  #
    http_server.listen(options.port)
    print "listener port %s .... ", options.port
    # signal.signal(signal.SIGTERM, sig_handler)
    # signal.signal(signal.SIGINT, sig_handler)

    tornado.ioloop.IOLoop.instance().start()
vieyahn2017 commented 4 years ago

nginx.conf


#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    #include       mime.types;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    #sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    #keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  0.0.0.0;

        #charset     utf-8;

        location / {
            include uwsgi_params;
            uwsgi_pass unix:/tmp/uwsgi.sock;
            #uwsgi_pass 127.0.0.1:8000;
        }

    }

}
vieyahn2017 commented 4 years ago

nginx-wsgi.conf


#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    #include       mime.types;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    #sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    #keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  0.0.0.0;

        #charset     utf-8;

        location / {
            include uwsgi_params;
            #uwsgi_pass unix:/tmp/uwsgi.sock;
            uwsgi_pass 127.0.0.1:8000;
        }

    }

}
vieyahn2017 commented 4 years ago

nginx-tornado.py


#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    upstream tornadoes {
        server 127.0.0.1:8000;
    }

    server {
        listen       80;
        server_name  0.0.0.0;

        #charset     utf-8;

        location / {
            proxy_pass_header Server;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Scheme $scheme;
            proxy_pass http://tornadoes;
        }

    }

}