【问题标题】:PyMySQL in Flask/Apache sometimes returning empty resultFlask/Apache 中的 PyMySQL 有时会返回空结果
【发布时间】:2016-03-14 03:38:05
【问题描述】:

我有一个 Flask 应用程序,在 Apache 中运行,它依赖于 PyMySQL。该应用程序提供了一系列 REST 命令。它在 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 调用 - 但它们基本上都做同样的事情(即 - 获取连接、创建游标、运行基本选择查询 - 有时确实有超过 2 个字段、执行查询、获取结果并将其作为 JSON 对象返回)。那里的提交应该是不必要的,但这似乎是 PyMySQL 的一个运行问题,导致获取旧数据。

问题是这些 REST 调用有时会返回空的 JSON 集(即[]),进一步调查表明,执行调用有时会返回完全空的结果,但不会引发异常。这种情况经常发生——但并非总是如此。一些调用确实成功返回值。当我尝试继续通话直到它返回结果时(如:

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

) 进程进入无限循环,最终(快速)阻止 apache 处理更多请求。

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

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

【问题讨论】:

    标签: python mysql apache flask pymysql


    【解决方案1】:

    您正在创建一个由您的应用程序使用的全局 mysql 连接,悬停 pymysql 声明一个为 1 的 threadsafety,根据 dbapi2 specification 意味着:

    1   Threads may share the module, but not connections. 
    

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

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

    • 为每个线程创建一个新连接,将其存储为线程本地以供进一步使用
    • 为每个请求创建一个新连接,将其存储在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 一起使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多