Как оптимизировать производительность приложения Flask
Введение
Flask — это легкая и гибкая веб-инфраструктура для создания приложений малого и среднего размера. Он обычно используется в проектах, начиная от простых личных блогов и заканчивая более сложными приложениями, такими как REST API, платформы SaaS, веб-сайты электронной коммерции и информационные панели, управляемые данными.
Однако по мере увеличения трафика или сложности вашего приложения вы можете начать замечать узкие места в производительности. Независимо от того, создаете ли вы систему управления контентом (CMS), API для мобильного приложения или инструмент визуализации данных в реальном времени, оптимизация производительности Flask становится решающей для обеспечения гибкого и масштабируемого пользовательского опыта.
В этом руководстве вы изучите различные методы и лучшие практики для оптимизации производительности приложения Flask.
Предварительные условия
Сервер под управлением Ubuntu и пользователь без полномочий root с привилегиями sudo и активным брандмауэром. Для получения инструкций по настройке выберите свой дистрибутив из этого списка и следуйте нашему руководству по первоначальной настройке сервера. Пожалуйста, убедитесь, что вы работаете с поддерживаемой версией Ubuntu.
-
Знакомство с командной строкой Linux. Чтобы познакомиться с командной строкой или освежить ее знания, вы можете посетить это руководство по основам командной строки Linux.
Базовое понимание программирования на Python.
В вашей системе Ubuntu установлен Python 3.7 или выше. Чтобы узнать, как запустить скрипт Python в Ubuntu, вы можете обратиться к нашему руководству «Как запустить скрипт Python в Ubuntu».
Настройка среды Flask
Ubuntu 24.04 по умолчанию поставляется с Python 3. Откройте терминал и выполните следующую команду, чтобы дважды проверить установку Python 3:
root@ubuntu:~# python3 --version
Python 3.12.3
Если Python 3 уже установлен на вашем компьютере, приведенная выше команда вернет текущую версию установки Python 3. Если он не установлен, вы можете запустить следующую команду и получить установку Python 3:
root@ubuntu:~# sudo apt install python3
Далее вам необходимо установить установщик пакета pip
в вашей системе:
root@ubuntu:~# sudo apt install python3-pip
После установки pip
давайте установим Flask.
Вы установите Flask через pip
. Рекомендуется делать это в виртуальной среде, чтобы избежать конфликтов с другими пакетами в вашей системе.
root@ubuntu:~# python3 -m venv myprojectenv
root@ubuntu:~# source myprojectenv/bin/activate
root@ubuntu:~# pip install Flask
Создайте приложение Flask
Следующим шагом будет написание кода Python для приложения Flask. Чтобы создать новый скрипт, перейдите в выбранный вами каталог:
root@ubuntu:~# cd ~/path-to-your-script-directory
Находясь внутри каталога, создайте новый файл Python, app.py
и импортируйте Flask. Затем инициализируйте приложение Flask и создайте базовый маршрут.
root@ubuntu:~# nano app.py
Откроется пустой текстовый редактор. Напишите здесь свою логику или скопируйте следующий код:
from flask import Flask, jsonify, request
app = Flask(__name__)
Simulate a slow endpoint
@app.route('/slow')
def slow():
import time
time.sleep(2) # to simulate a slow response
return jsonify(message="This request was slow!")
Simulate an intensive database operation
@app.route('/db')
def db_operation():
# This is a dummy function to simulate a database query
result = {"name": "User", "email": "user@example.com"}
return jsonify(result)
Simulate a static file being served
@app.route('/')
def index():
return "<h1>Welcome to the Sample Flask App</h1>"
if __name__ == '__main__':
app.run(debug=True)
Теперь давайте запустим приложение Flask:
root@ubuntu:~# flask run
Вы можете протестировать конечные точки с помощью следующих команд curl
:
Проверьте конечную точку /
(обслуживает статический контент):
root@ubuntu:~# curl http://127.0.0.1:5000/
[secondary_lebel Output]
<h1>Welcome to the Sample Flask App</h1>%
Проверьте конечную точку /slow
(имитирует медленный ответ):
root@ubuntu:~# time curl http://127.0.0.1:5000/db
Чтобы проверить эту медленную конечную точку, мы используем команду time
в Linux. Команда time
используется для измерения времени выполнения данной команды или программы. Он предоставляет три основные части информации:
- Реальное время: фактическое время, прошедшее от начала до конца команды.
- Время пользователя: количество времени процессора, затрачиваемого в пользовательском режиме.
- Системное время: количество времени процессора, затрачиваемого в режиме ядра.
Это поможет нам измерить фактическое время, затрачиваемое нашей медленной конечной точкой. Вывод может выглядеть примерно так:
{"message":"This request was slow!"}
curl http://127.0.0.1:5000/slow 0.00s user 0.01s system 0% cpu 2.023 total
Ответ на этот запрос занимает около 2 секунд, поскольку вызов time.sleep(2)
имитирует медленный ответ.
Давайте проверим конечную точку /db
(имитирует работу с базой данных):
root@ubuntu:~# curl http://127.0.0.1:5000/db
{"email":"user@example.com","name":"User"}
Протестировав эти конечные точки с помощью curl
, вы можете убедиться, что ваше приложение Flask работает правильно и что ответы соответствуют ожиданиям.
В следующем разделе вы научитесь оптимизировать производительность приложений с помощью различных методов.
Используйте готовый к работе сервер WSGI
Встроенный сервер разработки Flask не предназначен для производственных сред. Чтобы эффективно обрабатывать одновременные запросы, вам следует переключиться на готовый к работе сервер WSGI, например Gunicorn.
Установите и настройте Gunicorn
Давайте установим Gunicorn
root@ubuntu:~# pip install gunicorn
Запустите приложение Flask с помощью Gunicorn с 4 рабочими процессами:
root@ubuntu:~# gunicorn -w 4 -b 0.0.0.0:8000 app:app
% /Library/Python/3.9/bin/gunicorn -w 4 -b 0.0.0.0:8000 app:app
[2024-09-13 18:37:24 +0530] [99925] [INFO] Starting gunicorn 23.0.0
[2024-09-13 18:37:24 +0530] [99925] [INFO] Listening at: http://0.0.0.0:8000 (99925)
[2024-09-13 18:37:24 +0530] [99925] [INFO] Using worker: sync
[2024-09-13 18:37:24 +0530] [99926] [INFO] Booting worker with pid: 99926
[2024-09-13 18:37:25 +0530] [99927] [INFO] Booting worker with pid: 99927
[2024-09-13 18:37:25 +0530] [99928] [INFO] Booting worker with pid: 99928
[2024-09-13 18:37:25 +0530] [99929] [INFO] Booting worker with pid: 99929
[2024-09-13 18:37:37 +0530] [99925] [INFO] Handling signal: winch
^C[2024-09-13 18:38:51 +0530] [99925] [INFO] Handling signal: int
[2024-09-13 18:38:51 +0530] [99927] [INFO] Worker exiting (pid: 99927)
[2024-09-13 18:38:51 +0530] [99926] [INFO] Worker exiting (pid: 99926)
[2024-09-13 18:38:51 +0530] [99928] [INFO] Worker exiting (pid: 99928)
[2024-09-13 18:38:51 +0530] [99929] [INFO] Worker exiting (pid: 99929)
[2024-09-13 18:38:51 +0530] [99925] [INFO] Shutting down: Master
Вот преимущества использования Gunicorn:
- Одновременная обработка запросов: Gunicorn позволяет обрабатывать несколько запросов одновременно, используя несколько рабочих процессов.
- Балансировка нагрузки: балансирует входящие запросы между рабочими процессами, обеспечивая оптимальное использование ресурсов сервера.
- Асинхронные рабочие процессы. Благодаря асинхронным рабочим процессам, таким как
gevent
, он может эффективно обрабатывать длительные задачи, не блокируя другие запросы. - Масштабируемость. Gunicorn может масштабироваться горизонтально за счет увеличения количества рабочих процессов для обработки большего количества одновременных запросов.
- Отказоустойчивость. Он автоматически заменяет не отвечающих или вышедших из строя рабочих процессов, обеспечивая высокую доступность.
- Готовность к рабочей среде. В отличие от сервера разработки Flask, Gunicorn оптимизирован для производственных сред и обладает улучшенными функциями безопасности, стабильности и производительности.
Переключившись на Gunicorn для производства, вы можете значительно улучшить пропускную способность и скорость реагирования вашего приложения Flask, что сделает его готовым к эффективной обработке реального трафика.
Включите кэширование для снижения нагрузки
Кэширование — один из лучших способов повысить производительность Flask за счет сокращения избыточной обработки. Здесь вы добавите Flask-Caching
для кэширования результата маршрута /slow
.
Установите и настройте Flask-кэширование с помощью Redis
Установите необходимые пакеты:
root@ubuntu:~# pip install Flask-Caching redis
Обновите app.py
, чтобы добавить кеширование в маршрут /slow
.
Откройте редактор и обновите файл app.py
, указав следующее:
root@ubuntu:~# nano app.py
from flask_caching import Cache
app = Flask(__name__)
Configure Flask-Caching with Redis
app.config['CACHE_TYPE'] = 'redis'
app.config['CACHE_REDIS_HOST'] = 'localhost'
app.config['CACHE_REDIS_PORT'] = 6379
cache = Cache(app)
@app.route('/slow')
@cache.cached(timeout=60)
def slow():
import time
time.sleep(2) # Simulate a slow response
return jsonify(message="This request was slow!")
После первого запроса к /slow
последующие запросы в течение 60 секунд будут обслуживаться из кеша в обход функции time.sleep()
. Это снижает нагрузку на сервер и ускоряет время ответа.
Примечание. В этом руководстве мы используем localhost
в качестве хоста Redis. Однако в производственной среде рекомендуется использовать управляемый сервис Redis, например DigitalOcean Managed Redis. Это обеспечивает лучшую масштабируемость, надежность и безопасность для ваших потребностей в кэшировании. Вы можете узнать больше о целочислении DigitalOcean Managed Redis в приложении производственного уровня в этом руководстве по кэшированию с использованием DigitalOcean Redis на платформе приложений.
Чтобы проверить, кэшируются ли данные, давайте выполним приведенные ниже команды для конечной точки /slow
.
Это первый запрос к конечной точке /slow
. После завершения этого запроса результат маршрута /slow
кэшируется.
root@ubuntu:~# time curl http://127.0.0.1:5000/slow
{"message":"This request was slow!"}
curl http://127.0.0.1:5000/slow 0.00s user 0.01s system 0% cpu 2.023 total
Это последующий запрос к конечной точке /slow
в течение 60 секунд:
root@ubuntu:~# time curl http://127.0.0.1:5000/slow
{"message":"This request was slow!"}
curl http://127.0.0.1:5000/slow 0.00s user 0.00s system 0% cpu 0.015 total
Оптимизация запросов к базе данных
Запросы к базе данных часто могут стать узким местом производительности. В этом разделе вы смоделируете оптимизацию запросов к базе данных с помощью SQLAlchemy и пула соединений.
Имитация запроса к базе данных с помощью пула соединений
Сначала давайте установим SQLAlchemy
root@ubuntu:~# pip install Flask-SQLAlchemy
Обновите app.py
, чтобы настроить пул соединений.
rom flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
Simulate an intensive database operation
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_POOL_SIZE'] = 5 # Connection pool size
db = SQLAlchemy(app)
@app.route('/db1')
def db_operation_pooling():
# Simulate a database query
result = db.session.execute(text('SELECT 1')).fetchall()
return jsonify(result=str(result))
Теперь, когда мы выполняем запрос curl
к маршруту db1
, мы должны заметить следующий вывод:
root@ubuntu:~# curl http://127.0.0.1:5000/db1
output{"result":"[(1,)]"}
Вы можете значительно оптимизировать производительность вашего приложения Flask, внедрив пул соединений в производственной среде. Пул соединений позволяет приложению повторно использовать существующие соединения с базой данных вместо создания новых для каждого запроса. Это снижает затраты на установление новых соединений, что приводит к сокращению времени отклика и улучшению масштабируемости.
Конфигурация SQLALCHEMY_POOL_SIZE
, которую мы установили ранее, ограничивает количество подключений в пуле. Это значение необходимо настроить в производственной среде с учетом ваших конкретных требований и возможностей сервера. Кроме того, вы можете рассмотреть другие параметры пула, такие как SQLALCHEMY_MAX_OVERFLOW
, чтобы разрешить дополнительные соединения, когда пул заполнен, и SQLALCHEMY_POOL_TIMEOUT
, чтобы указать, как долго запрос будет ожидать соединения.
Помните: хотя в нашем примере для простоты используется SQLite, в реальном сценарии вы, скорее всего, будете использовать более надежную базу данных, такую как PostgreSQL или MySQL. Эти базы данных имеют свои собственные механизмы объединения пулов соединений, которые можно использовать в сочетании с пулами SQLAlchemy для еще большей производительности.
Тщательно настроив и используя пул соединений, вы можете гарантировать, что ваше приложение Flask эффективно обрабатывает операции с базой данных даже при высокой нагрузке, что значительно повышает его общую производительность.
Включить сжатие Gzip
Сжатие ответов может значительно сократить объем данных, передаваемых между вашим сервером и клиентами, повышая производительность.
Установите и настройте Flask-Compress
Давайте установим пакет Flask-compress
.
root@ubuntu:~# pip install Flask-Compress
Далее давайте обновим app.py
, чтобы включить сжатие.
from flask_compress import Compress
This below command enables Gzip compression for the Flask appIt compresses responses before sending them to clients,reducing data transfer and improving performance
Compress(app)
@app.route('/compress')
def Compress():
return "<h1>Welcome to the optimized Flask app !</h1>"
Это автоматически сжимает ответы размером более 500 байт, сокращая время передачи больших ответов.
В производственной среде сжатие Gzip может значительно сократить объем данных, передаваемых между вашим сервером и клиентами, особенно для текстового контента, такого как HTML, CSS и JavaScript.
Такое сокращение объема передачи данных приводит к ускорению загрузки страниц, улучшению пользовательского опыта и снижению затрат на полосу пропускания. Кроме того, многие современные веб-браузеры автоматически поддерживают распаковку Gzip, что делает его широко совместимым методом оптимизации. Включив сжатие Gzip, вы можете эффективно улучшить производительность и масштабируемость вашего приложения Flask, не требуя каких-либо изменений на стороне клиента.
Разгрузить интенсивные задачи на Celery
Для ресурсоемких операций, таких как отправка электронных писем или обработка больших наборов данных, лучше всего разгрузить их на фоновые задачи с помощью Celery
. Это предотвращает блокировку входящих запросов длительными задачами.
Celery — это мощная распределенная система очередей задач, которая позволяет асинхронно выполнять трудоемкие задачи. Переложив интенсивные операции на Celery, вы можете значительно улучшить скорость реагирования и масштабируемость вашего приложения Flask. Celery делегирует задачи рабочим процессам, которые могут выполняться на отдельных машинах, что позволяет лучше использовать ресурсы и выполнять параллельную обработку.
Ключевые преимущества использования сельдерея включают в себя:
- Улучшено время ответа на запросы пользователей.
- Улучшенная масштабируемость и управление ресурсами.
- Возможность решать сложные, трудоемкие задачи без блокировки основного приложения.
- Встроенная поддержка планирования задач и повтора неудачных задач.
- Простая интеграция с различными брокерами сообщений, такими как RabbitMQ или Redis.
Используя Celery, вы можете гарантировать, что ваше приложение Flask останется отзывчивым даже при выполнении задач с интенсивными вычислениями или задачами, связанными с вводом-выводом.
Настройте Celery для фоновых задач
Давайте установим Celery
.
root@ubuntu:~# pip install Celery
Далее давайте обновим app.py
, чтобы настроить Celery для асинхронных задач:
from celery import Celery
celery = Celery(app.name, broker='redis://localhost:6379/0')
@celery.task
def long_task():
import time
time.sleep(10) # Simulate a long task
return "Task Complete"
@app.route('/start-task')
def start_task():
long_task.delay()
return 'Task started'
В отдельном терминале запустите рабочий Celery:
root@ubuntu:~# celery -A app.celery worker --loglevel=info
------------- celery@your-computer-name v5.2.7 (dawn-chorus)
--- ***** -----
-- ******* ---- Linux-x.x.x-x-generic-x86_64-with-glibc2.xx 2023-xx-xx
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app: app:0x7f8b8c0b3cd0
- ** ---------- .> transport: redis://localhost:6379/0
- ** ---------- .> results: disabled://
- *** --- * --- .> concurrency: 8 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. app.long_task
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] Connected to redis://localhost:6379/0
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] mingle: searching for neighbors
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] mingle: all alone
[2023-xx-xx xx:xx:xx,xxx: INFO/MainProcess] celery@your-computer-name ready.
Теперь запустите команду curl
, чтобы перейти по маршруту /start-task
, результат будет таким:
root@ubuntu:~# curl http://127.0.0.1:5000/start-task
Task started
Это вернет "Задача запущена" почти мгновенно, даже если фоновая задача все еще выполняется.
Функция start_task()
делает две вещи:
Он вызывает
long_task.delay()
, который асинхронно запускает задачу Celery. Это означает, что задача ставится в очередь для выполнения в фоновом режиме, но функция не ждет ее завершения.Он немедленно возвращает строку «Задача запущена».
Важно отметить, что фактическая длительная задача (моделируемая 10-секундным спящим режимом) выполняется Celery асинхронно. Маршрут Flask не ждет завершения этой задачи, прежде чем ответить на запрос.
Итак, когда вы свернете эту конечную точку, вы получите немедленный ответ «Задача запущена», в то время как фактическая задача продолжает выполняться в фоновом режиме в течение 10 секунд.
Через 10 секунд после завершения фоновой задачи вы должны заметить следующее сообщение журнала:
Вывод будет похож на этот:
[2024-xx-xx xx:xx:xx,xxx: INFO/MainProcess] Task app.long_task[task-id] received
[2024-xx-xx xx:xx:xx,xxx: INFO/ForkPoolWorker-1] Task app.long_task[task-id] succeeded in 10.xxxs: 'Task Complete'
В этом примере показано, как Celery повышает производительность приложения Flask за счет асинхронной обработки долго выполняющихся задач, сохраняя при этом отзывчивость основного приложения. Длительная задача будет выполняться в фоновом режиме, освобождая приложение Flask для обработки других запросов.
В производственной среде внедрение Celery включает в себя:
- Использование надежного брокера сообщений, такого как RabbitMQ.
- Использование выделенного механизма обработки результатов (например, PostgreSQL)
- Управление работниками с помощью систем управления процессами (например, супервайзер)
- Внедрение инструментов мониторинга (например, Flower)
- Улучшение обработки и регистрации ошибок
- Использование приоритезации задач
- Масштабирование с использованием нескольких рабочих процессов на разных машинах
- Обеспечение надлежащих мер безопасности
Заключение
В этом руководстве вы узнали, как оптимизировать приложение Flask, реализовав различные методы повышения производительности. Выполнив эти шаги, вы сможете улучшить производительность, масштабируемость и скорость реагирования вашего приложения Flask, гарантируя его эффективную работу даже при большой нагрузке.