我正在尝试使用我的烧瓶应用程序中的gevent运行多个并发下游请求。
我有:
import gevent
from gevent import monkey
monkey.patch_all(thread=False)
from flask import Flask, request, g
app = Flask(__name__, static_folder='static')
和以下代码:
def f1(self):
@copy_current_request_context
def _test(t):
time.sleep(t)
r = requests.get(
'https://webhook.site/d14a41a6-9c6a-4da0-bbe6-3cc660daea3d', params=dict(word='test')
)
return r.status_code
jobs = [gevent.spawn(_test, 5), gevent.spawn(_test, 10)]
results = [job.value for job in gevent.joinall(jobs)]
return None
如果第二个_test花费的时间超过了第一个,那么我会收到Popped错误的应用上下文错误。
如果我添加另一种方法,如:
@copy_current_request_context
def _test_bis(t):
from random import randint
time.sleep(t)
r = requests.get(
'https://webhook.site/d14a41a6-9c6a-4da0-bbe6-3cc660daea3d', params=dict(word='test')
)
return r.status_code
并使用它:
jobs = [gevent.spawn(_test_bis, 5), gevent.spawn(_test, 10)]
我没有任何错误。
知道如何解决这个问题?
答案 0 :(得分:2)
Thi与Flask's application context有关。当使用spawn旋转多个Greenlet时,Flask不知道哪个应用程序是“当前”应用程序。您已经使用了@copy_current_request_context
装饰器,但是,它仅复制请求上下文并为所有bg线程提供新的应用程序上下文。
为避免这种情况,您可以创建一个以闭包形式传递当前应用上下文:
def copy_current_app_context(f):
from flask.globals import _app_ctx_stack
appctx = _app_ctx_stack.top
def _(*args, **kwargs):
with appctx:
return f(*args, **kwargs)
return _
或者您可以选择将线程代码包装在test_request_context
中,以便您可以访问本地上下文:
def f1(self):
with app.test_request_context():
def _test(t):
time.sleep(t)
r = requests.get(
'https://webhook.site/d14a41a6-9c6a-4da0-bbe6-3cc660daea3d', params=dict(word='test')
)
return r.status_code
jobs = [gevent.spawn(_test, 5), gevent.spawn(_test, 10)]
results = [job.value for job in gevent.joinall(jobs)]
return None
值得指出的是,线程将具有与原始请求不同的上下文。如果您需要任何有趣的请求数据,在产生线程之前将其提取是很重要的。