При использовании gevent.spawn возник неправильный контекст приложения - PullRequest
0 голосов
/ 29 мая 2018

Я пытаюсь выполнить несколько одновременных нисходящих запросов, используя 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 голосов
/ 14 июля 2018

Это связано с контекстом приложения Flask .Когда вы запускаете несколько гринлетов с помощью spawn, 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

стоит отметить, что поток будет иметь другой контекст, чем исходный запрос.если вам нужны какие-либо интересные данные запроса, важно извлечь их, прежде чем создавать поток.

...