Flask/Apache 中的 PyMySQL 有时会返回空结果

PyMySQL in Flask/Apache sometimes returning empty result

我有一个 Flask 应用程序,在 Apache 中是 运行,它依赖于 PyMySQL。该应用程序提供了一系列 REST 命令。 运行ning 下 Python 3.

在不提供完整源代码的情况下,程序结构如下:

#!flask/bin/python
import json
import pymysql
from flask import *

# Used to hopefully share the connection if the process isn't restarted
mysql_connection = None   

# Gets the mysql_connection, or opens it
GetStoreCnx():
    global mysql_connection
    if (mysql_connection != None):
        store_connection_string = ""
        # Get the connection string from the config file
        with open('config/storedb.json', 'r') as f:
            store_connection_string = json.load(f)
        mysql_connection = pymysql.connect(**store_connection_string)
    return mysql_connection;


class Server(Flask):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

# Return results via REST
@app.route('/results1', methods=['GET'])
def get_result1():
    cnx = GetStoreCnx();
    cursor = cnx.cursor();
    query = """
        SELECT 
            result_name,
            successful
        FROM
            results
        """
    cursor.execute(query)
    cnx.commit()
    result = cursor.fetchall()
    return json.dumps(result)

# Run server, if needed
if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

还有一些 REST 调用 - 但它们本质上都做同样的事情(即 - 获取连接、创建游标、运行 基本 select 查询 - 有时确实有超过 2 个字段,执行查询,获取结果并将其 return 作为 JSON 对象)。那里的提交应该是不必要的,但这似乎是 PyMySQL 的一个 运行ning 问题,导致获取旧数据。

问题是这些 REST 调用有时 return 空 JSON 集(即 []),进一步调查表明执行调用有时 return 完全空结果但不抛出异常。这种情况经常发生——但并非总是如此。一些调用成功地执行 return 值。当我尝试保持通话直到结果 return 时(如:

while(cursor.execute(query) < 1):
    pass

) 该进程进入无限循环,最终(迅速)阻止 apache 为任何更多请求提供服务。

服务器(目前)每秒仅服务大约 5 个调用。如果我使用开发服务器,则不会出现此问题。

有什么办法可以防止这个错误吗?是 PyMySQL 的错误吗?我正在做的事情阻止了与 MySQL 的正确连接吗?

您正在创建一个供您的应用程序使用的全局 mysql 连接,但是 pymysql 声明一个 threadsafety of 1, which according to the dbapi2 specification 意味着:

1   Threads may share the module, but not connections. 

由于 flask 中的并发请求将由不同的线程处理,因此您不应共享连接。使用开发服务器时没有遇到任何问题的原因是它运行单线程。

要避免这种情况,您可以:

  • 为每个线程创建一个新连接,将其存储为线程本地以供进一步使用
  • 为每个请求创建一个新连接,将其存储在flask.g中以供进一步使用

为此,您的 GetStoreCnx 函数可以这样更改:

import threading
thread_local = threading.local()

def GetStoreCnx():        
    if not hasattr(thread_local, 'mysql_connection'):
        store_connection_string = ""
        # Get the connection string from the config file
        with open('config/storedb.json', 'r') as f:
            store_connection_string = json.load(f)
        mysql_connection = pymysql.connect(**store_connection_string)
        thread_local.mysql_connection = mysql_connection
    return thread_local.mysql_connection;

SQLAlchemy 的 scoped_session() 做了类似的事情。对于每个请求一个连接,这也应该与 flask.g 而不是 thread_local 一起使用。