Пишем асинхронное web-приложение на Python3, конфигурация

newbie

Опубликован:  2020-12-30T05:59:18.916496Z
Отредактирован:  2021-01-02T06:22:03.055873Z
1
0
0
Вы неавторизованы, рекомендую зарегистрироваться и авторизоваться.

Мир очень быстро меняется, и ещё быстрей меняется 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 - тестовая ветка свободной операционной системы.

bT75ajB5ic.png

Почему Debian bullseye? Работа над проектом скорей всего займёт некоторый продолжительный период времени, а если разработка ведётся в две руки и фронт и бэк, то до продакшна проект доберётся скорей всего, когда bullseye уже будет заморожен и, возможно, отрелизится. Определённо, buster к тому времени уже устареет. К тому же в bullseye актуальная на данный момент версия Питона, что тоже на руку...

Итак, как видно на снимке экрана выше, Питон в Debian bullseye установлен из коробки даже в самом минимальном наборе инсталлятора netinstall. Тем не менее, поскольку в процессе работы над проектом мне потребуется виртуальное окружение, а некоторые пакеты при установке в виртуальное окружение потребуют компиляции из исходного кода, я установлю в систему дополнительно три пакета: python3-pip, python3-venv и dkms, которые вытащат из репы по зависимостям весь необходимый для разработки набор. Кроме этого, мне определённо потребуется система контроля версий, поэтому я установлю ещё и git.

sudo apt install python3-pip python3-venv dkms git

lEmNU5IdSq.png

Жму enter и дожидаюсь завершения установки всех запрошенных пакетным менеджером пакетов.

Замечание: Git обычно требует начальной настройки, этот вопрос рассмотрен в одном из выпусков этого блога.

Чуть позже, для проекта потребуется ещё несколько программ, их я буду устанавливать по мере необходимости, а пока моя операционная система готова к бою...

3. Базовый каталог и виртуальное окружение

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

mkdir -p workspace/auriz

NjVHlVo7GW.png

Этот адрес и будет базовым каталогом web-приложения, над разработкой которого я сосредоточу своё внимание в этом цикле статей. Поскольку у этого приложения будет некоторое количество Python-зависимостей - дополнительных программ, разработанных другими программистами, мне нужно эти зависимости установить. Установить я их могу либо в системные каталоги операционной системы, что не очень хорошо, либо в виртуальное окружение. Создаю его.

python3 -m venv venv

rsY00ateHj.png

Как видно на снимке экрана выше, в базовом каталоге появился новый каталог с именем venv в полном соответствии с введённой командой. В этом каталоге и хранятся файлы и каталоги виртуального окружения auriz. Активирую его.

source venv/bin/activate

dpNO50h7lF.png

На снимке экрана выше следует обратить внимание на то, как изменилось приглашение командной строки в терминале - подчёркнуто красным фломастером. Префикс приглашения командной строки указывает на имя активного в этом терминале виртуального окружения. Чтобы с установкой пакетов в это виртуальное окружение не случилось непредвиденных траблов, обновлю wheel.

pip install --upgrade wheel

GtwoBa3yVp.png

На текущем этапе разработки проекта мне необходимы два дополнительных пакета: Starlette и Uvicorn. Устанавливаю их.

pip install Starlette uvicorn

rixtXmm0kw.png

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

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

В итоге дерево базового каталога будет выглядеть следующим образом.

vqGmaCNp7E.png

Пришло время написать первый код на Питоне. Начнём с конфигурации приложения, открываю в текстовом редакторе инит вложенного каталога 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

H60y5yPFlH.png

Файл настроек .env я создам в базовом каталоге приложения, мне нужно будет протестировать, что этот файл подвязан правильно, поэтому пока он будет иметь минимальное содержание.

KSbEBBnzkH.png

Теперь необходимо разработать первую функцию представления, открываю в текстовом редакторе файл 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>')

C2kPNgVUqP.png

Теперь всё это великолепие необходимо как-то запустить... Для этого я создал файл runserver.py, открываю его в текстовом редакторе и пишу такой код:

import uvicorn

if __name__ == '__main__':
    uvicorn.run(
        'auriz:app', host='127.0.0.1',
        reload=True, port=5000, log_level='info')

EwurULmOOe.png

Стартовая страница, несмотря на убогость её текущего состояния, готова, нужно её протестировать, об этом далее...

5. Запуск сервера

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

python runserver.py

lUyCNo3vst.png

Открываю web-браузер и пробую постучаться по указанному в первой строчке выхлопа сервера в консоль адресу: http://127.0.0.1:5000.

jl0ICfE8GP.png

Как видно на снимке экрана, имя сайта отображенное в браузере соответствует имени сайта в файле настроек .env, значит, приложение правильно получает свои настройки. Прервать работу сервера можно сочетанием ctrl+c в терминале с активным процессом сервера.

ncZVkjbf1L.png

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

В следующих выпусках этого блога я опишу в деталях некоторые важные аспекты разработки web-приложения auriz и, вероятно, покажу некоторые приёмы отладки кода приложения, а эта стартовая страница очень скоро получит более детальную разметку со стилями, картинками и сценариями JavaScript.

6. Сохраняем проект в Git

Поскольку приложение сконфигурировано и работает в соответствии с планом на текущий этап разработки, мне необходимо сохранить это состояние кода в Git и запушить его на Git-сервер. Для этого понадобится учётная запись на github.com или любом другом альтернативном Git-сервере, я буду использовать гитхаб.

Иду на страницу сервера, и создаю в своём профиле новую репу с именем auriz, а на странице созданного репозитория нахожу инструкцию для подключения к этой репе. Из этой инструкции копирую в буфер обмена одну строчку.

kyS23MeB5G.png

Возвращаюсь в терминал с активным виртуальным окружением auriz и инициирую в базовом каталоге приложения локальный репозиторий Git.

git init .

Точка в конце команды обязательна, она указывает на текущий каталог.

OMUsFQdO7u.png

Копирую в этот терминал из буфера обмена команду из инструкции github.com и выполняю её.

git remote add origin https://github.com/newbie-c/auriz.git

mvolJajh7f.png

Поскольку в базовом каталоге есть файлы и каталоги, которые в репе на сервере Git мне не нужны, создаю в базовом каталоге файл с именем .gitignore - точка в начале имени обязательна, она определяет файл как скрытый, открываю его в текстовом редакторе и вписываю все имена каталогов и файлов, которые не следует копировать в репозиторий.

QzZ47GbMC9.png

Как видно на снимке экрана выше, файл с настройками приложения .env попал в исключения и в открытую репу на github.com копироваться не будет, так как в перспективе этот файл будет содержать некоторые настройки с конфиденциальными данными. Пока в этом файле ничего конфиденциального нет, я могу скопировать его с новым именем как шаблон, чтобы иметь возможность восстанавливать файл .env из этого шаблона.

cp .env env.template

5dFhFg1wcg.png

Посмотрим на состояние репы.

git status

BiATpYgRRr.png

Добавляю все текущие изменения в файлах и каталогах приложения для коммита.

git add .

Детально посмотреть все изменения, которые будут добавлены в следующий коммит, можно с помощью следующей команды:

git diff --staged

bwk9fP6Uke.png

Делаю коммит.

git commit -m"Create the project"

Jtqxb5A8iU.png

И запушиваю состояние локального репозитория Git на сервер.

git push -u origin master

YDZwMaXgqF.png

Посмотреть состояние репозитория можно по этой ссылке.

7. Продолжение следует

В следующем выпуске блога я покажу конфигурацию каталога статики, получу из сети Интернет необходимые библиотеки для разработки фронтенда приложения и скопирую их в этот каталог. Будет интересно..

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

Комментарии: