Пишем асинхронное web-приложение на Python3, конфигурация
newbie
Опубликован: | 2020-12-30T05:59:18.916496Z |
Отредактирован: | 2021-01-02T06:22:03.055873Z |
Мир очень быстро меняется, и ещё быстрей меняется web, методы и приёмы разработки web-приложений и используемые для этого инструменты. Когда-то я старательно осваивал Flask и SQLAlchemy и думал, что с этим фреймворком и его набором инструментов благополучно доживу до старости. Но нет... колесо должно крутиться, иначе белочка замёрзнет.
Этот обзор открывает цикл статей, посвященных web-разработке с асинхронным фреймворком общего назначения Starlette. В этом цикле статей я намерен повторить и описать начальную стадию разработки web-приложения, которое работает на этом сайте. Я пока не знаю, насколько длинным окажется этот цикл, всё зависит от количества читателей и их активности в комментариях, но первый блупринт - систему авторизации пользователей я твёрдо намерен описать.
Под спойлерами ниже и в следующих статьях цикла я буду демонстрировать некоторые приёмы работы со Starlette и базами данных PostgreSQL и Redis в процессе работы над так называемым MVC web-приложением. Кроме этого, я конечно же уделю должное внимание и вопросам frontend-разработки. Последующие статьи цикла будут изобиловать кривыми велосипедами и собственными решениями некоторых сложных прикладных задач. Эмоционально нестабильным читателям, которые точно знают, как нужно программировать web, и как делать нельзя, я не советую читать дальше, потому что в этом цикле статей я буду делать так, как мне хочется.
1. Целесообразность миграции на Starlette
Web-приложение этого сайта изначально было написано на Flask с использованием PostgreSQL и SQLAlchemy. Flask - отличный инструмент, классно продуман, удобен, гибок и эффективен, но у него есть один существенный недостаток - это блокирующий фреймворк, а отсюда следует сразу целый букет неприятностей, с которыми разработчик вынужден бороться.
Первая важная неприятность - с блокирующим фреймворком необходимо детально продумывать логику функции представления, чтобы на обработку запроса сервер тратил как можно меньше времени. Не всегда это возможно, и поэтому приходится иметь дело с фоновыми задачами и инструментом для их обслуживания - Celery.
Вторая неприятность следует из первой, Celery - достаточно капризный инструмент, его сложно конфигурировать, с ним неудобно работать, ему необходимы дополнительные программы для организации брокера и резалтбэкенда, и впоследствии на проде он существенно нагружает RAM сервера.
В условиях, когда Python стал асинхронным, а сайт вынужден довольствоваться VDS с весьма скромными техническими параметрами, я просто не мог не обратить свой взор на асинхронные web-фреймворки общего назначения. Их много: aiohttp, Sanic, Quart, Starlette, Vibora etc., и мне пришлось выбирать. Я выбрал сначала Sanic, но уже на начальном этапе столкнулся с проблемой обслуживания статики. На aiohttp я долго и внимательно смотрел, но у этого фреймворка ужасная документация, написанная на ломаном, корявом English. Поэтому мой взор упал на Starlette.
Starlette - классный фреймворк, не так изощрённо продуман как Flask, но тем не менее вполне удобен и эффективен, имеет качественно написанную и продуманную документацию, в процессе работы мне почти не приходилось лезть в исходник. Миграция проекта с некоторыми функциональными доработками заняла почти шесть месяцев, учитывая full stack и только две руки - это не много. При разработке мне слегка мешали некоторые привычки и стереотипы, которыми я разжился при работе с Flask, но их благополучно удалось преодолеть. В целом могу сказать, что асинхронный web писать проще, он требует меньше телодвижений и слегка экономит ресурсы сервера на проде. А как это состоялось, я попробую описать в этом цикле статей...
2. Установка необходимых для разработки программ
Начнём с элементарного... посмотрим на установку необходимого для работы над проектом программного обеспечения. Для этого у меня есть подключенный к сети Интернет компьютер, на котором установлен великолепный Debian bullseye - тестовая ветка свободной операционной системы.
Почему Debian bullseye? Работа над проектом скорей всего займёт некоторый продолжительный период времени, а если разработка ведётся в две руки и фронт и бэк, то до продакшна проект доберётся скорей всего, когда bullseye уже будет заморожен и, возможно, отрелизится. Определённо, buster к тому времени уже устареет. К тому же в bullseye актуальная на данный момент версия Питона, что тоже на руку...
Итак, как видно на снимке экрана выше, Питон в Debian bullseye установлен из коробки даже в самом минимальном наборе инсталлятора netinstall. Тем не менее, поскольку в процессе работы над проектом мне потребуется виртуальное окружение, а некоторые пакеты при установке в виртуальное окружение потребуют компиляции из исходного кода, я установлю в систему дополнительно три пакета: python3-pip, python3-venv и dkms, которые вытащат из репы по зависимостям весь необходимый для разработки набор. Кроме этого, мне определённо потребуется система контроля версий, поэтому я установлю ещё и git.
sudo apt install python3-pip python3-venv dkms git
Жму enter
и дожидаюсь завершения установки всех запрошенных пакетным менеджером пакетов.
Замечание: Git обычно требует начальной настройки, этот вопрос рассмотрен в одном из выпусков этого блога.
Чуть позже, для проекта потребуется ещё несколько программ, их я буду устанавливать по мере необходимости, а пока моя операционная система готова к бою...
3. Базовый каталог и виртуальное окружение
Поскольку разрабатываемое web-приложение впоследствии получит определённую структуру каталогов и некоторое количество текстовых файлов различных форматов, всё это великолепие необходимо где-то хранить. Создаю в рабочей зоне своего домашнего каталога директорию с именем разрабатываемого приложения и вхожу в неё.
mkdir -p workspace/auriz
Этот адрес и будет базовым каталогом web-приложения, над разработкой которого я сосредоточу своё внимание в этом цикле статей. Поскольку у этого приложения будет некоторое количество Python-зависимостей - дополнительных программ, разработанных другими программистами, мне нужно эти зависимости установить. Установить я их могу либо в системные каталоги операционной системы, что не очень хорошо, либо в виртуальное окружение. Создаю его.
python3 -m venv venv
Как видно на снимке экрана выше, в базовом каталоге появился новый каталог с именем venv
в полном соответствии с введённой командой. В этом каталоге и хранятся файлы и каталоги виртуального окружения auriz. Активирую его.
source venv/bin/activate
На снимке экрана выше следует обратить внимание на то, как изменилось приглашение командной строки в терминале - подчёркнуто красным фломастером. Префикс приглашения командной строки указывает на имя активного в этом терминале виртуального окружения. Чтобы с установкой пакетов в это виртуальное окружение не случилось непредвиденных траблов, обновлю wheel.
pip install --upgrade wheel
На текущем этапе разработки проекта мне необходимы два дополнительных пакета: Starlette и Uvicorn. Устанавливаю их.
pip install Starlette uvicorn
Впоследствии, на следующих этапах разработки я буду устанавливать в это виртуальное окружение дополнительные необходимые зависимости, а пока всё готово к созданию конфигурации проекта и разработке первой функции представления, об этом далее...
4. Конфигурация проекта
Проект предполагается достаточно объёмный, чтобы не запутаться в его файлах и каталогах лучше бы уже на начальном этапе разработки продумать приемлемую структуру проекта. Мне нужен ещё один вложенный каталог с тем же именем, создаю его.
mkdir auriz
В нём я выделю отдельный каталог для первого блупринта с именем main.
mkdir auriz/main
Конфигурация приложения будет храниться в файле __init__.py
в каталоге auriz.
touch auriz/__init__.py
В каталоге блупринта main я создам два файла, в файле с именем views.py
будет храниться код функций представления этого блупринта.
touch auriz/main/__init__.py
touch auriz/main/views.py
И, наконец, в базовом каталоге приложения мне нужен будет модуль для запуска отладочного сервера.
touch runserver.py
В итоге дерево базового каталога будет выглядеть следующим образом.
Пришло время написать первый код на Питоне. Начнём с конфигурации приложения, открываю в текстовом редакторе инит вложенного каталога auriz.
vim auriz/__init__.py
Пишу в него такой код:
import os
from starlette.applications import Starlette
from starlette.config import Config
from starlette.routing import Route
# импорт первой функции представления, которая пока не существует
# и будет создана чуть позже
from .main.views import show_index
# определяем имя базового каталога приложения с
# привязкой к текущему файлу
base = os.path.dirname(__file__)
# определяем файл настроек приложения и получаем из него
# текущие настройки
settings = Config(os.path.join(os.path.dirname(base), '.env'))
# создаём экземпляр Starlette - текущее web-приложение
app = Starlette(
debug=settings.get('DEBUG', cast=bool),
routes=[Route('/', show_index, name='index')])
# привязываем текущие настройки в свойство приложения
app.config = settings
Файл настроек .env
я создам в базовом каталоге приложения, мне нужно будет протестировать, что этот файл подвязан правильно, поэтому пока он будет иметь минимальное содержание.
Теперь необходимо разработать первую функцию представления, открываю в текстовом редакторе файл views.py
из каталога main и пишу следующий код.
from starlette.responses import HTMLResponse
# определяю функцию представления стартовой страницы приложения
# на текущем этапе она будет весьма примитивна
async def show_index(request):
# мне нужно убедиться, что настройки приложения из файла .env
# доступны в каждом запросе приложения, получаю одну из настроек
name = request.app.config.get('SITE_NAME')
# формирую и возвращаю ответ сервера
return HTMLResponse(f'<p>{name}, стартовая страница.</p>')
Теперь всё это великолепие необходимо как-то запустить... Для этого я создал файл runserver.py
, открываю его в текстовом редакторе и пишу такой код:
import uvicorn
if __name__ == '__main__':
uvicorn.run(
'auriz:app', host='127.0.0.1',
reload=True, port=5000, log_level='info')
Стартовая страница, несмотря на убогость её текущего состояния, готова, нужно её протестировать, об этом далее...
5. Запуск сервера
Чтобы убедиться, что всё работает как задумано, и, в случае необходимости, отладить код приложения, запускаю сервер, находясь в базовом каталоге приложения в терминале с активированным виртуальным окружением.
python runserver.py
Открываю web-браузер и пробую постучаться по указанному в первой строчке выхлопа сервера в консоль адресу: http://127.0.0.1:5000
.
Как видно на снимке экрана, имя сайта отображенное в браузере соответствует имени сайта в файле настроек .env
, значит, приложение правильно получает свои настройки. Прервать работу сервера можно сочетанием ctrl+c
в терминале с активным процессом сервера.
Важное замечание: uvicorn после запуска и в процессе тестирования страниц web-приложения в браузере выдаёт в консоль важную для процесса отладки и тестирования информацию, которая здорово помогает решать некоторые заковыристые задачи, выхлоп сервера в консоль следует научиться читать, на него я обращу внимание ещё не один раз.
В следующих выпусках этого блога я опишу в деталях некоторые важные аспекты разработки web-приложения auriz и, вероятно, покажу некоторые приёмы отладки кода приложения, а эта стартовая страница очень скоро получит более детальную разметку со стилями, картинками и сценариями JavaScript.
6. Сохраняем проект в Git
Поскольку приложение сконфигурировано и работает в соответствии с планом на текущий этап разработки, мне необходимо сохранить это состояние кода в Git и запушить его на Git-сервер. Для этого понадобится учётная запись на github.com
или любом другом альтернативном Git-сервере, я буду использовать гитхаб.
Иду на страницу сервера, и создаю в своём профиле новую репу с именем auriz, а на странице созданного репозитория нахожу инструкцию для подключения к этой репе. Из этой инструкции копирую в буфер обмена одну строчку.
Возвращаюсь в терминал с активным виртуальным окружением auriz и инициирую в базовом каталоге приложения локальный репозиторий Git.
git init .
Точка в конце команды обязательна, она указывает на текущий каталог.
Копирую в этот терминал из буфера обмена команду из инструкции github.com
и выполняю её.
git remote add origin https://github.com/newbie-c/auriz.git
Поскольку в базовом каталоге есть файлы и каталоги, которые в репе на сервере Git мне не нужны, создаю в базовом каталоге файл с именем .gitignore
- точка в начале имени обязательна, она определяет файл как скрытый, открываю его в текстовом редакторе и вписываю все имена каталогов и файлов, которые не следует копировать в репозиторий.
Как видно на снимке экрана выше, файл с настройками приложения .env
попал в исключения и в открытую репу на github.com
копироваться не будет, так как в перспективе этот файл будет содержать некоторые настройки с конфиденциальными данными. Пока в этом файле ничего конфиденциального нет, я могу скопировать его с новым именем как шаблон, чтобы иметь возможность восстанавливать файл .env
из этого шаблона.
cp .env env.template
Посмотрим на состояние репы.
git status
Добавляю все текущие изменения в файлах и каталогах приложения для коммита.
git add .
Детально посмотреть все изменения, которые будут добавлены в следующий коммит, можно с помощью следующей команды:
git diff --staged
Делаю коммит.
git commit -m"Create the project"
И запушиваю состояние локального репозитория Git на сервер.
git push -u origin master
Посмотреть состояние репозитория можно по этой ссылке.
7. Продолжение следует
В следующем выпуске блога я покажу конфигурацию каталога статики, получу из сети Интернет необходимые библиотеки для разработки фронтенда приложения и скопирую их в этот каталог. Будет интересно..
Все посвященные разработке auriz выпуски блога можно отфильтровать в хронологическом порядке по соответствующей метке - auriz.