MySQLデータベースにクエリを実行し、さまざまなリクエストパラメータに基づいて各リクエストの結果を表示するシンプルなシングルページFlask(v0.8)アプリケーションがあります。アプリケーションは、Nginx上のTornadoを使用して提供されます。
最近、DBクエリがまだ実行されているときに、アプリケーションが異なるクライアントからの同時リクエストをブロックしているように見えることに気付きました。例-
- クライアントは、完了するまでに時間がかかる(> 20秒)複雑なDBクエリを使用して要求を行います。
- 別のクライアントがサーバーにリクエストを送信し、最初のクエリが返されるまでブロックされます。
したがって、基本的に、アプリケーションはすべての人にサービスを提供する単一のプロセスのように動作します。サーバー上の共有DB接続に問題があると思っていたのでdbutils
、接続プール用のモジュールを使い始めました。それは役に立ちませんでした。サーバーのアーキテクチャや構成に何か大きなものが欠けていると思うので、フィードバックをいただければ幸いです。
これは、dbクエリを実行するFlaskのコードです(簡略化)。
#... flask imports and such
import MySQLdb
from DBUtils.PooledDB import PooledDB
POOL_SIZE = 5
class DBConnection:
def __init__(self):
self.pool = PooledDB(MySQLdb,
POOL_SIZE,
user='admin',
passwd='sikrit',
host='localhost',
db='data',
blocking=False,
maxcached=10,
maxconnections=10)
def query(self, sql):
"execute SQL and return results"
# obtain a connection from the pool and
# query the database
conn = self.pool.dedicated_connection()
cursor = conn.cursor()
cursor.execute(sql)
# get results and terminate connection
results = cursor.fetchall()
cursor.close()
conn.close()
return results
global db
db = DBConnection()
@app.route('/query/')
def query():
if request.method == 'GET':
# perform some DB querying based query params
sql = process_request_params(request)
results = db.query(sql)
# parse, render, etc...
竜巻ラッパー(run.py
)は次のとおりです。
#!/usr/bin/env python
import tornado
from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from myapplication import app
from tornado.options import define, options
define("port", default=8888, help="run on the given port", type=int)
def main():
tornado.options.parse_command_line()
http_server = HTTPServer(WSGIContainer(app), xheaders=True)
http_server.listen(options.port)
IOLoop.instance().start()
if __name__ == '__main__': main()
起動スクリプトを介してアプリを起動します。
#!/bin/sh
APP_ROOT=/srv/www/site
cd $APP_ROOT
python run.py --port=8000 --log_file_prefix=$APP_ROOT/logs/app.8000.log 2>&1 /dev/null
python run.py --port=8001 --log_file_prefix=$APP_ROOT/logs/app.8001.log 2>&1 /dev/null
そしてこれはnginxの構成です:
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
}
http {
upstream frontends {
server 127.0.0.1:8000;
server 127.0.0.1:8001;
}
include /usr/local/nginx/conf/mime.types;
default_type application/octet-stream;
# ..
keepalive_timeout 65;
proxy_read_timeout 200;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
gzip on;
gzip_min_length 1000;
gzip_proxied any;
gzip_types text/plain text/html text/css text/xml application/x-javascript
application/xml application/atom+xml text/javascript;
proxy_next_upstream error;
server {
listen 80;
root /srv/www/site;
location ^~ /static/ {
if ($query_string) {
expires max;
}
}
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://frontends;
}
}
}
これは非常に小さなクライアントベースにサービスを提供する小さなアプリケーションであり、そのほとんどは私が継承したレガシーコードであり、修正や書き換えを行うことはありませんでした。問題に気付いたのは、完了するのに時間がかかるより複雑なクエリタイプを追加した後です。何かが飛び出した場合は、フィードバックをいただければ幸いです。ありがとう。