首页 > 解决方案 > 使用 gevent.spawn 时弹出错误的应用程序上下文

问题描述

我正在尝试在我的烧瓶应用程序中使用 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 花费的时间比第一个长,我会收到弹出错误的应用程序上下文错误。

如果我添加另一种方法,例如:

    @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)]

我没有任何错误。

知道如何解决这个问题吗?

标签: pythonflaskgevent

解决方案


这与Flask 的应用程序上下文有关。当您使用 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 _

或者您可以选择将线程代码包装在 a 中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

值得指出的是,线程将具有与原始请求不同的上下文。如果您需要任何有趣的请求数据,在生成线程之前提取这些数据很重要。


推荐阅读