为什么 o Flask-SocketIO(使用 Gevent)在查询数据库时暂停?

Why o Flask-SocketIO (With Gevent) is paused when a query to db is made?

我正在为我的公司开发一个基于网络的应用程序。最近我遇到了 'basic' 问题,无法将 SQL 脚本写入数据库 (OracleDB)。

我正在使用异步模式='gevent' 的 Flask-SocketIO 网络服务器,显然当您执行 cx_Oracle.connection.cursor.execute() 时,会阻塞我的整个应用程序,直到响应 returns(网络服务器停止接收其他请求)。

我一直在寻找问题的答案,我意识到 cx_Oracle 与其他客户和请求并不 运行 平行。

问题示例:

from gevent import monkey; monkey.patch_all()
from flask_socketio import SocketIO
from flask import Flask
import cx_Oracle

app = Flask(__name__, template_folder='templates')

app.secret_key = 'testing'
app.config['DEBUG'] = False

socketio = SocketIO(app, async_mode='gevent')

@app.route('/')
def index():
    sql_query = 'select * from blabla'
    connection = cx_Oracle.connect(user, password, host, threaded=True)
    cursor = connection.cursor()
    cursor.execute(sql_query)
    transacoes = cursor.fetchall()

socketio.run(app, host='localhost', port=5005)

当我向 http://localhost/ 发出超过 1 个请求时,我的应用程序不会响应 2+ 个请求,直到第一个请求完成。

我尝试实现 gevent.ThreadPool 以并行进行 1 个以上的查询,但我遇到了问题:

带有gevent.ThreadPool的代码示例:

from gevent import monkey; monkey.patch_all()
from gevent.threadpool import ThreadPool
from flask_socketio import SocketIO
from flask import Flask
import cx_Oracle

app = Flask(__name__, template_folder='templates')

app.secret_key = 'testing'
app.config['DEBUG'] = False

socketio = SocketIO(app, async_mode='gevent')

def receive_data(user, password, host, sql_query):
    connection = cx_Oracle.connect(user, password, host, threaded=True)
    cursor = connection.cursor()
    cursor.execute(sql_query)
    response = cursor.fecthall()
    cursor.close()
    connection.close()
    return response

@app.route('/')
def index():
    sql_query = 'select * from blabla'

    pool = ThreadPool(1) # I tried with more than 100
    async_result = pool.apply_async(receive_data,
                                    args=(user, password, host, sql_query))
    transacoes = async_result.get()


socketio.run(app, host='localhost', port=5005)

在 receive_data() 中发出多个请求时出现错误:

RuntimeError: Working outside of application context.

This typically means that you attempted to use functionality that needed to interface with the current application object in a way. To solve this set up an application context with app.app_context(). See the documentation for more information.

并且:

'LoopExit: This operation would block forever

为了解决这个问题,我将 async_mode='gevent' 更改为 async_mode='threading' 并删除了 monkey_patch()

我不知道是什么影响了我的应用程序,但所有系统显然都运行正常。

我找到了这个问题的另一个解决方案。

当模块不支持 monkey_path 时,eventlet 建议您使用 eventlet.tpool http://eventlet.net/doc/threading.html.

示例:

from eventlet import tpool
cur = tpool.execute(cx_Oracle.connect, connection_string, *args, **kwargs)

这解决了主要问题,现在我可以使用 socketio 和 "async_mode=eventlet"。