使用gevent.spawn时弹出错误的应用程序上下文

时间:2018-05-29 13:21:44

标签: python flask gevent

我正在尝试使用我的烧瓶应用程序中的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)]

我没有任何错误。

知道如何解决这个问题?

1 个答案:

答案 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

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