我有一个简单的单页 Flask (v0.8) 应用程序,它查询 MySQL 数据库并根据不同的请求参数显示每个请求的结果。该应用程序使用 Tornado over Nginx 提供服务。
最近我注意到当数据库查询仍在运行时,应用程序似乎阻止了来自不同客户端的并发请求。例如 -
- 客户端使用复杂的数据库查询发出请求,该查询需要一段时间才能完成(> 20 秒)。
- 另一个客户端向服务器发出请求并被阻塞,直到第一个查询返回。
所以基本上,应用程序的行为就像一个为每个人服务的单一进程。我在想问题出在服务器上的共享数据库连接上,所以我开始使用该dbutils模块进行连接池。那没有帮助。我想我可能在架构或服务器配置中遗漏了一些重要的东西,所以我很感激对此的任何反馈。
这是执行数据库查询(简化)的 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;
    }
  }
}
这是一个小型应用程序,服务于非常小的客户群,其中大部分是我继承的遗留代码,从未修复或重写。我只是在添加更复杂的查询类型后才注意到这个问题,这些查询类型需要更长的时间才能完成。如果有任何问题,我会很感激您的反馈。谢谢。