У меня есть веб-сервис, который запускает длительные задания (порядка нескольких часов). Я разрабатываю это, используя Flask, Gunicorn и nginx.

Я думаю о том, чтобы иметь маршрут, который занимает много времени, вызвать функцию, которая создает поток. Затем функция вернет guid обратно к маршруту, а маршрут вернет URL (используя guid), который пользователь может использовать для проверки прогресса. Я делаю поток демоном (thread.daemon = True), чтобы поток завершался, если мой вызывающий код завершается (неожиданно).

Это правильный подход к использованию? Это работает, но это не значит, что это правильно.

my_thread = threading.Thread(target=self._run_audit, args=())
my_thread.daemon = True
my_thread.start()
22
Mark 16 Дек 2015 в 23:58

4 ответа

Лучший ответ

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

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

from flask import Flask

app = Flask(__name__)
app.config.update(
    CELERY_BROKER_URL='redis://localhost:6379',
    CELERY_RESULT_BACKEND='redis://localhost:6379'
)
celery = make_celery(app)


@celery.task()
def add_together(a, b):
    return a + b

Тогда вы можете запустить:

>>> result = add_together.delay(23, 42)
>>> result.wait()
65

Просто помните, что вам нужно запустить работника отдельно:

celery -A your_application worker
9
Ali Nikneshan 26 Май 2018 в 03:52

Ваш подход хорош и будет полностью работать, но зачем изобретать фоновый рабочий для веб-приложений на Python, когда существует широко распространенное решение, а именно сельдерея.

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

Плюс сельдерей дает вам такие функции, как постоянство задач и возможность распределять рабочих по нескольким машинам.

2
Robert Moskal 16 Дек 2015 в 21:11

Ну, хотя ваш подход не является неправильным, в основном это может привести к тому, что в вашей программе закончатся доступные потоки. Как упомянул Али, общий подход заключается в использовании очередей заданий, таких как RQ или Celery , Однако вам не нужно извлекать функции для использования этих библиотек. Для Flask я рекомендую вам использовать Flask-RQ. Начать просто:

RQ

pip install flask-rq

Просто не забудьте установить Redis, прежде чем использовать его в своем приложении Flask.

И просто используйте @Job Decorator в ваших функциях Flask:

from flask.ext.rq import job


@job
def process(i):
    #  Long stuff to process


process.delay(3)

И, наконец, вам нужно rqworker, чтобы запустить работника:

rqworker

Вы можете найти документы RQ для получения дополнительной информации. RQ предназначен для простых длительных процессов.

Сельдерей

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

Greenlets

У гринлетов есть выключатели. Позвольте вам переключаться между длительными процессами. Вы можете использовать гринлеты для запуска процессов. Преимущество заключается в том, что вам не нужен Redis и другие работники, вместо этого вы должны изменить дизайн своих функций, чтобы они были совместимы:

from greenlet import greenlet

def test1():
    print 12
    gr2.switch()
    print 34

def test2():
    print 56
    gr1.switch()
    print 78

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
6
Community 23 Май 2017 в 12:25

Сельдерей и RQ - это слишком трудоемкая задача для простой задачи . Посмотрите эти документы - https://docs.python.org/3/ библиотека / concurrent.futures.html

Также посмотрите пример того, как запускать задания в фоновом режиме для приложения Flask в фоновом режиме - https://stackoverflow.com/a/39008301/5569578

27
Denys Synashko 2 Дек 2017 в 06:02