@BaldUser
Инженеришка

Как разобраться с многопоточностью и асинхронностью в веб-приложении?

Несколько месяцев назад я начал писать своё первое веб-приложение. Его хочу использовать для анализа некой независимо (отдельным демоном) собираемой статистики от клиентов и её отображения в понятный и простой вид. Поскольку я не профи, выбор стека технологий был попроще: Flask как простой и гибкий фремворк, sqlite как готовая из коробки субд и matplotlib для отрисовки картинок. Со временем захотелось это приложение сделать многопользовательским, чтобы в перспективе монетизировать. Авторизацию прикрутил модулем Flask-login, это не было сложно, а вот далее пошли проблемы.
1) Я так и не понял: Flask однопоточный или многопоточный? Синхронный или асинхронный? Когда на одной странице я хочу вставить 2 графика, которые рисуются на лету по данным из БД, sqlite ругается, что "Recursive use of cursors not allowed". То есть получается, к нему пытаются обратиться за данными из таблицы 2 потока. Ладно, проблему с БД можно решить переходом на нормальную СУБД вроде postgres, или временно использовать библиотеку sqlite3worker (чтобы сериализовать запросы к sqlite), как это сделано сейчас. Но тут проблема №2
2) Matplotlib тоже не рисует 2 графика одновременно, приложение просто закрывается без объявления ошибки (1 график вроде успевает отрисовать). Видимо, тоже не работает в многопоточном режиме, на их сайте так и сказано: "Matplotlib is not thread-safe".
3) Хочу реализовать очередь заданий - то есть запускать процесс, рисующий графики, отдельно (например тоже демоном или как RestAPI) и кидать ему из своего приложения задания в очередь, а он чтобы их по очереди доставал, делал и отдавал. Но пока не могу разобраться в куче вариантов, и вообще туда ли я смотрю.

а) Блокировать создание потоков во Flask я не могу, потому что у меня в коде многопоточности нет. Он меня не спрашивает.
б) Если matplotlib не thread-safe, может на создание каждого графика запускать из приложения отдельный процесс? Кто-нибудь так делал? Так вообще делают в серьёзных приложениях?
в) Вариант с очередью на Celery или RQ интересный, но пока глаза разбегаются от названий. Celery, RQ, какие-то gevent, ForkingMixin, ThreadingMixing. Мне нужно просто чтобы графики строились по очереди. Чем это проще сделать? Может, вообще взять голый Redis?
г) Ну и самое интересное: пока не поздно, есть ли смысл развивать проект на Flask, или мне взять многопоточный фреймворк типа FastAPI, aiohttp и т.п.? Что посоветуете? На сайте flask в секции becoming big ( https://flask.palletsprojects.com/en/1.1.x/becomingbig/ ) есть разные советы, но они для профи, типа subclass или read the source. А вообще какие критерии big? Я планирую развиваться в будущем, но начинаю с малого.

Особенно интересуют ответы на последние два пункта - чем лучше реализовать очередь и стоит ли продолжать проект на Flask?
  • Вопрос задан
  • 883 просмотра
Решения вопроса 1
romesses
@romesses
Backend инженер
1a) https://stackoverflow.com/a/38876915
подробнее о режиме поточности

As of Flask 1.0, the WSGI server included with Flask is run in threaded mode by default.

Prior to 1.0, or if you disable threading, the server is run in single-threaded mode, and can only handle one request at a time. Any parallel requests will have to wait until they can be handled, which can lead to issues if you tried to contact your own server from a request.

With threaded=True requests are each handled in a new thread. How many threads your server can handle concurrently depends entirely on your OS and what limits it sets on the number of threads per process. The implementation uses the SocketServer.ThreadingMixIn class, which sets no limits to the number of threads it can spin up.

Note that the Flask server is designed for development only. It is not a production-ready server. Don't rely on it to run your site on the wider web. Use a proper WSGI server (like gunicorn or uWSGI) instead.

1b) Насчет ограничений Sqlite: попробуйте, как указано в https://stackoverflow.com/a/26630550

2-3) для построения графиков воспользуйтесь очередями. Учтите, RQ работает только на POSIX-совместимой ОС (WSL на Windows). Воркеры очередей так и работают - на отдельных процессах. Из Flask кладут в очередь, а в воркере - берут из нее. И так для каждого графика или другой задачи - просто класть задачу в очередь и продолжать далее. В сообщении задачи нужно указать идентификатор и параметры для построения графика, а также все, что вам потребуется для дальнейшего занесения данных в БД.

пока не поздно, есть ли смысл развивать проект на Flask, или мне взять многопоточный фреймворк типа FastAPI, aiohttp и т.п.? Что посоветуете?
Попробуйте с очередями сначала. У вас же не тысячи пользователей, похоже.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
sergey-gornostaev
@sergey-gornostaev Куратор тега Многопоточность
Седой и строгий
Flask синхронный и однопоточный. Конкурентность в нём реализуется запуском нескольких процессов web-приложения. Естественно, при таком подходе web-приложение не должно иметь состояния, в идеале быть 12-факторным.
Ответ написан
Комментировать
@javedimka
Хочу сока
В каком режиме работает приложение на Фласк определяется wsgi сервером.

Recursive use of cursors not allowed
Означает не то что ты написал.

Судя по остальным вопросам намудрил ты в своём приложении жести, по типу запуска потоков в вьюхах и прочего

Без кода сложно будет ответить.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы