wsgi python что это
Введение в WSGI-серверы: Часть первая
Данная статья является переводом статьи Кевина Голдберга «An Introduction to Python WSGI Servers: Part 1» blog.appdynamics.com/engineering/an-introduction-to-python-wsgi-servers-part-1 с небольшими дополнениями от переводчика
Краткая история серверов WSGI Python
WSGI-серверы появились потому, что веб-серверы в то время не умели взаимодействовать с приложениями, написанными на языке Python. WSGI (произносится как «whiz-gee» с твердым «g») был разработан Филиппом Дж. Эби (вместе с Ян Бикинг и др.) В начале 2000-х годов. Модуль Apache, известный как mod_python, разработанный Григорием Трубецким в конце 90-х годов, на тот момент обрабатывал большую часть Python-приложений. Однако mod_python не был официальной спецификацией. Он был просто создан, чтобы разработчики могли запускать код Python на сервере. К сожалению, такой подход был небезопасным и разработчики начали искать новое решение.
WSGI(Web-Server Gateway Interface) является потомком CGI(Common Gateway Interface). Когда веб начал развиваться, CGI разрастался из-за поддержки огромного количества языков и из-за отсутствия других решений. Однако, такое решение было медленным и ограниченным. WSGI был разработан как интерфейс для маршрутизации запросов от веб-серверов(Apache, Nginx и т.д.) на веб-приложения.
Сервер и веб-приложение
В простейшем случае WSGI состоит из двух основных сущностей:
Принцип работы:
Веб-сервер исполняет код и отправляет связанную с http-запросом информацию и callback-функцию в веб-приложение. Затем запрос на стороне приложения обрабатывается и высылается ответ на веб-сервер.
Периодически между веб-сервером и веб-приложением существуют одна или несколько промежуточных прослоек. Такие прослойки позволяют осуществить, например, балансировку между несколькими веб-приложениеми или предпроцессинг(предобработку) отдаваемого контента.
Вот примеры фреймворков, поддерживающих WSGI:
Почему именно WSGI?
Возможно Вы спросите «Хорошо, но почему именно WSGI?». На это есть несколько причин:
Bjoern
Bjoern — это асинхронный WSGI-сервер, написанный на C. Написанный с нуля, чтобы быть небольшим и быстрым, он был разработан с использованием http_parser от Райана Даля (создателя Node.js) и цикла событий Libev от Марка Леманна.
С размером загрузки всего 18 КБ он состоит из менее 800 строк кода. Он занимает менее 1 МБ оперативной памяти и не использует корутины или потоки. Bjoern полностью совместим с WSGI и считается одним из самых высокопроизводительных WSGI-серверов.
Bjoern поддерживает постоянные соединения и может привязываться к Unix-сокетам или TCP-адресам. Bjoern считается более быстрым, чем Gunicorn, и менее раздутым, чем uWSGI и Meinheld. Один из недостатков данного сервера — отсутствие нормальной документации с понятными примерами.
uWSGI
uWSGI был разработан компанией Unbit(Италия) с целью стать fullstack-решением, способным создавать услуги хостинга. Он назван в честь стандарта WSGI и создавался как плагин для одного из проектов компании.
Широкий и постоянно развивающийся проект, uWSGI позволяет делать гораздо больше, чем веб-приложения для хостинга. Многие находят uWSGI мощным инструментом, в то время как другие считают его несколько раздутым. Начиная с версии 2.0 uWSGI поддерживает также запуск веб-приложений на языках Lua, Perl, Ruby и других.
mod_wsgi
mod_wsgi — модуль HTTP-сервера Apache, разработанный Грэмом Дамплтоном (ранее, один из разработчиков mod_python), предоставляет интерфейс WSGI для веб-приложений. Совместим с версиями языка Python2 и Python3. Создан в качестве альтернативы другим решениям для интеграции веб-приложений, таких как CGI, FastCGI и mod_python. Может быть установлен как модуль Apache или через mod_wsgi express. Второй способ упрощает установку для разработчиков Python, которые не так хорошо знакомы с Apache. Другие преимущества:
• Вы не нуждаетесь в root-правах при полной установке.
• Создается локальная среда, что снижает риск негативного воздействия на существующие настройки.
Развитие mod_wsgi как проекта может показаться медленным из-за того, что модуль разрабатывается одним разработчиком. Другим недостатком является то, что документация в настоящее время плохо организована и может быть устаревшей.
В настоящее время основное внимание уделяется упрощению реализации Apache с использованием mod_wsgi в средах с использованием Docker.
Meinheld
Созданный Ютака Мацубара, Meinheld является сервером, совместимым с WSGI, который использует мощь picoev и greenlet для выполнения асинхронных операций ввода-вывода. Он может использоваться с автономным HTTP-сервером или через Gunicorn.
Meinheld имеет зависимость от стороннего компонента, называемого greenlet.
Репозиторий с исходным кодом расположен на GitHub. Meinheld поддерживает веб-сокеты и включает в себя несколько monkeypatches над другими модулями для расширения функциональности.
CherryPy
CherryPy — более известен как минималистичный Python-фреймворк для написания веб-приложений, CherryPy также поставляется с WSGI thread-pooled веб-сервером и поддержкой протокола HTTP / 1.1. Команда разработчиков CherryPy описывает свой веб-сервер как production-ready, высокоскоростной, thread-pooled HTTP-сервер. Он имеет:
• Быструю, простую настройку;
• Возможность масштабирования;
• Небольшое и простое в использовании решение для ваших приложений Python;
• Поддержка SSL.
CherryPy отличает себя от более известных WSGI-серверов из-за простоты использования и удобства для разработчиков. Вы можете написать небольшое веб-приложение в течении нескольких минут, запустив его в несколько процессов и создав только один файл с именем server.py. Объединив CherryPy с Nginx в качестве прокси-сервера, Вы получите надежный способ обслуживания своих веб-приложений. CherryPy был создан Реми Делоном. Он хотел создать структуру, которая была бы максимально близка к главным принципам разработки на языке Python.
Gunicorn
Gunicorn — это WSGI-сервер, созданный для использования в UNIX-системах. Название — сокращенная и комбинированная версия слов «Green Unicorn». На самом сайте проекта есть зеленый единорог. Gunicorn был перенесен из проекта «Unicorn» из языка Ruby. Он относительно быстрый, ресурсоёмкий, легко запускается и работает с широким спектром веб-фреймворков.
Команда разработчиков рекомендует использовать Gunicorn в связке с Nginx, где Nginx используется в качестве прокси-сервера.
Впервые было опубликовано в журнале «Системный администратор» #12 за 2008 год
Если вы разрабатываете Web-приложение, каркас для разработки Web-приложений, или даже Web-сервер на Python вам просто необходимо знание основ протокола WSGI — стандартного способа связи Web-сервера и Web-приложения.
Описание протокола
WSGI (расшифровывается как Web Server Gateway Interface — интерфейс шлюза Web-сервера) — это простой и универсальный интерфейс взаимодействия между Web-сервером и Web-приложением, впервые описанный в PEP-333 (http://www.python.org/dev/peps/pep-0333/). Под простотой в данном случае подразумевается лишь простота подключения приложения, но не простота реализации Web-приложений для авторов. Надо заметить, что основной целью разработки WSGI была разработка простого протокола, который бы мог разделить выбор каркасов для разработки Web-приложений от выбора Web-серверов. Это, в частности, позволяет разработчикам приложений (каркасов) и серверов концентрироваться на своей области специализации и отличает WSGI от более общих протоколов связи приложений с Web-серверами, таких как CGI, или FastCGI. С точки зрения WSGI цельное Web-приложение делится на две части: сервер (или шлюз) и непосредственно приложение (или каркас для построения приложений). Для обращения к приложению серверная часть использует вызываемый объект (это может быть функция, метод, класс, или экземпляр класса с методом __call__). WSGI также позволяет создавать приложения-посредники которые являются приложением для Web-сервера и сервером для Web-приложения. Такие посредники могут использоваться для предварительной обработки запросов к приложению, или последующей обработки его ответов. Ниже мы рассмотрим несколько примеров использования WSGI и затем обратимся к деталям протокола.
Сторона приложения
Как уже говорилось выше приложение — это просто вызываемый объект, который принимает два аргумента. Приложения должны допускать возможность многократных вызовов, что является обычной ситуацией практически для всех серверов (исключая вызовы с помощью CGI). Ниже представлены два примера приложения. Первое приложение реализовано в виде функции:
Здесь функция использует второй аргумент для передачи статуса и заголовков ответа и затем возвращает тело ответа в виде списка строк. Второе приложение реализовано в виде класса:
В данном случае объект класса будет представлять из себя итератор который на первом шаге итерации использует второй аргумент для передачи статуса и заголовков ответа и затем вернет тело ответа.
Надо отметить, что приложение с точки зрения WSGI — это всего лишь точка входа через которую сервер получает доступ к Web-приложению, или каркасу для построения Web-приложений.
Сторона сервера
Сервер (или шлюз) будет вызывать приложение для каждого HTTP запроса который ему предназначен. Для примера ниже представлен упрощенный шлюз CGI — WSGI. Пример использует упрощенную обработку ошибок т.к. по умолчанию ошибки будут выдаваться на sys.stderr и затем записываться в лог Web-сервера. Вызываемый объект приложения в данном случае передается как параметр функции:
Посредник: сервер и приложение в одном
Как уже было замечено выше некоторые объекты могут играть сразу две роли — быть сервером для какого-либо приложения и приложением для сервера. Вот примеры некоторых ситуаций для которых могут быть полезны WSGI-посредники:
Присутствие посредника в большинстве случае прозрачно и для сервера и для приложения, более того, посредники можно располагать один за другим, составляя таким образом «стек посредников».
Детали протокола
Как мы уже видели приложение должно принимать два аргумента, которые были названы environ и start_response, но могут иметь любые другие имена.
Первый параметр (environ) должен быть объектом словаря (dict) Python и содержит переменные среды похожие на переменные CGI. Этот объект также должен содержать обязательные для WSGI параметры, которые мы подробнее рассмотрим ниже, и может содержать переменные специфичные для конкретного Web-сервера.
Второй параметр (start_response) — это вызываемый объект которым приложение предваряет возвращение тела ответа и принимающий два обязательных параметра и один необязательный. Первый параметр (status) — статус ответа в виде строки, например «200 Ok». Второй параметр (response_headers в примере выше) — список кортежей (tuples) вида (имя_заголовка, значение_заголовка). Третий, необязательный параметр (exc_info) должен использоваться только при обработке ошибок и должен быть кортежем который возвращает функция sys.exc_info(). Надо заметить, что start_response не посылает заголовки сразу, а откладывает их до получения первой части тела ответа, что бы в случае ошибки их можно было заменить на заголовки сопутствующие ошибке. При этом start_response можно вызывать несколько раз только если передается третий параметр (exc_info).
После вызова сервером вызываемый объект приложения должен вернуть итерируемый объект возвращающий ноль, или несколько строк. Как мы видели в примерах выше этого можно достичь несколькими способами, например вернув список строк, или объект-итератор. При получении очередной части ответа сервер посылает ее клиенту без буферизации, но приложения могут осуществлять буферизацию ответа собственными силами.
В случае если итерируемый объект имеет метод close() он будет вызван по окончании обработки ответа сервером, даже в случае ошибки. Таким образом метод close() может использоваться для закрытия всех ресурсов приложения которые могли быть задействованы при создании ответа.
Переменные словаря environ
Словарь environ может содержать следующие CGI переменные: (Переменные которые может содержать словарь environ)
Имя | Наличие | Описание |
---|---|---|
REQUEST_METHOD | Обязательный | Метод запроса, например «GET», или «POST» |
SCRIPT_NAME | Может быть пустым | Начальная порция пути в URL соответствующая объекту приложения. |
PATH_INFO | Может быть пустым | Остаток пути в URL соответствующий цели запроса внутри приложения |
QUERY_STRING | Может быть пустым, или отсутствовать | Часть URL которая следует за «?» |
CONTENT_TYPE | Может быть пустым, или отсутствовать | Содержимое заголовка Content-Type в HTTP запросе |
CONTENT_LENGTH | Может быть пустым, или отсутсвовать | Содержимое заголовка Content-Length в HTTP запросе |
SERVER_NAME | Обязательный | Имя сервера |
SERVER_PORT | Обязательный | Порт сервера |
SERVER_PROTOCOL | Обязательный | Версия протокола который использует клиент для посылки запроса, например «HTTP/1.0», или «HTTP/1.1» |
HTTP_переменные | Необязательны | Переменные соответствующие заголовкам запроса переданным клиентом |
Также словарь environ должен содержать следующие, обязательные для WSGI переменные: (Обязательные для WSGI переменные в словаре environ)
Имя | Описание |
---|---|
wsgi.version | Кортеж (1, 0) представляющий версию WSGI — 1.0 |
wsgi.url_scheme | Строка представляющая схему из URL, обычно «http», или «https» |
wsgi.input | Объект похожий на файл из которого может быть прочитано тело запроса |
wsgi.errors | Объект похожий на файл в который приложение может выводить сообщения об ошибках |
wsgi.multithread | True если объект приложения может быть одновременно вызван из нескольких потоков |
wsgi.multiprocess | True если соответствующие объекты приложения могут быть одновременно вызваны в нескольких процессах |
wsgi.run_once | True если сервер предполагает (но не гарантирует), что приложение будет вызвано только один раз во время жизни текущего процесса |
Плюс environ может содержать специфичные для конкретного сервера переменные и переменные среды. Что бы увидеть набор переменных передаваемых приложению можно использовать, например, следующее простое WSGI приложение:
Обработка ошибок
В общем случае приложение должно обрабатывать внутренние ошибки и выводить соответствующее сообщение клиенту. Конечно, прежде чем выводить сообщение об ошибке приложение не должно начинать выводить нормальный ответ. Что бы обойти эту ситуацию используется третий параметр start_response — exc_info:
Если приложение еще не начало вывод тела ответа когда произошло исключение, то вызов start_response в обработчике будет нормальным и клиенту вернется ответ об ошибке. В случае если на момент ошибки уже был начат вывод ответа start_response выкинет переданное исключение. Это исключение не должно обрабатываться обработчиком, а будет обработано сервером и записано в журнал ошибок.
Пакет wsgiref
В стандартной библиотеке Python 2.5 появился новый пакет wsgiref, предоставляющий различные утилиты для упрощения работы с WSGI. Рассмотрим кратко его содержимое:
Что это такое? Простое WSGI-приложение
Начиная с этого занятия мы с вами будем знакомиться с весьма популярным и фреймворком Flask, который позволяет создавать сайты самых разных типов с использованием языка Python. Flask относится к разряду микрофреймворков, то есть, он предоставляет лишь базовый инструментарий для построения сайтов, как говорится, все по минимуму – ничего лишнего. Однако, этого вполне достаточно, чтобы создавать большинство типовых сайтов с поддержкой шаблонов страниц, баз данных и прочими стандартными возможностями. И, кроме того, простота позволяет достаточно быстро разобраться в этом пакете и начать сразу его применять. Наверное, в этом его главная фишка – простота и достаточность функционала для типовых решений.
Для тех кто не совсем знаком с принципом взаимодействия между клиентом (браузером) и фреймворком, установленном на сервере, опишу в двух словах этот процесс.
Когда пользователь вводит в браузер строку запроса, например, vk.com, то от браузера отправляется запрос к серверу, где расположен и работает этот сайт. Здесь мы отложим в сторону вопрос маршрутизации и DNS-серверов, сейчас это неважно, главное, что сеть Интернет так устроена, что маршрутизаторы «знают» куда направлять запросы, если они относятся к работающим сайтам.
Сервер постоянно находится в режиме ожидания очередного запроса и как только он приходит, формирует ответ клиенту, как правило, в виде HTML-документа. Этот документ возвращается в браузер и пользователь видит на экране устройства заветную страницу.
Но где же во всей этой схеме фреймворк? В действительности он установлен на сервере. Так как это обычный компьютер (ну может не совсем обычный, но принцип тот же), то на нем установлено соответствующее программное обеспечение. Мы, опять же, не будем здесь глубоко вдаваться в подробности, скажу лишь, что на них часто устанавливают Linux-подобные ОС (благодаря их надежности), затем программу под названием веб-сервер (часто это Apache или Nginx) и уже он отдает обработку запроса конкретному фреймворку:
Здесь WSGI (Web Server Gateway Interface) — стандарт взаимодействия между Python-программой, выполняющейся на стороне сервера, и самим веб-сервером, например Apache. Фактически, это интерпретатор Python, который запускает WSGI-приложение, написанное на Flask.
При поступлении запроса активизируется WSGI-приложение, выполняется определенный обработчик, который еще называется «Представление» и реализованный в виде функции на языке Python. Соответственно, если приходит сразу несколько запросов, то одна и та же функция-обработчик может быть запущена в параллельных потоках. Многопоточность – это норма для фреймворков, поэтому, работая с представлениями во Flask, всегда следует это учитывать.
Конечно, в рамках наших занятий мы не будем использовать удаленный сервер и устанавливать на него данный фреймворк – это отдельная задача. Кстати, современные хостеры предоставляют инструментарий для простой установки и настройки Flask. Поэтому этот процесс не представляет больших сложностей. А для изучения данного пакета на домашнем ПК от вас потребуется только его установить, используя установщик pip:
Теперь мы можем написать свое первое WSGI-приложение. В самом простом варианте оно выглядит так:
Вначале идет импорт класса Flask, который, фактически и формирует это приложение. Далее, мы создаем экземпляр этого класса и первым аргументом должны указать имя нашего приложения. Если вся программа пишется в одном файле, то следует передавать директиву __name__, которая в случае импорта будет содержать имя текущего файла, а в случае самостоятельного запуска – значение «__main__». Для Flask это имеет принципиальное значение, в частности, от этого зависит где искать подкаталоги с шаблонами и статичными документами.
После этого выполняется запуск фреймворка методом run и в качестве параметра указывается debug=True, чтобы мы в браузере видели все ошибки, которые будут возникать при разработке сайта-приложения. Конечно, после его создания, здесь следует прописать debug=False, чтобы случайные ошибки реальный пользователь уже не видел.
И, наконец, условие. Зачем оно? Смотрите, когда мы непосредственно запускаем наш модуль, то директива __name__ будет принимать значение «__main__» и будет запущен локальный веб-сервер для отладки текущего приложения. Если же модуль запускается, например, на удаленном сервере, то нам не нужно запускать еще один сервер. В этом случае директива __name__ будет принимать имя данного модуля и строчка app.run выполнена не будет. То есть, мы это условие прописали с целью запуска приложения непосредственно на локальном устройстве.
Соответственно, как только фреймворк запущен, у нас активизируется локальный веб-сервер и мы можем в браузере создавать запрос, используя вот такой начальный адрес:
Давайте сделаем это и посмотрим, что получится. Наберем в браузере указанный запрос и видим, что запрашиваемая страница не найдена:
Все верно, так и должно быть, так как мы в программе не создали еще ни одного представления. Сделаем и это, добавим его:
Здесь используется специальный декоратор route, который создает обертку вокруг нашей функции index, которая будет активизироваться при обращении к главной странице сайта, то есть, по запросу http://127.0.0.1:5000/
Запустим программу, обновим страницу и теперь в браузере видим то, что возвратила функция index:
Часто к главной странице обращаются еще по index, то есть:
Чтобы одну и ту же страницу отобразить по нескольким URL-адресам, следует добавить несколько конструкций route:
Соответственно, для любого другого адреса мы также можем добавить свой отдельный обработчик, прописав еще один декоратор route:
Теперь на нашем сайте как бы две страницы: главная и /about – о сайте. Причем, наши обработчики возвращают HTML-документ и все теги будут соответственно отображаться на странице в браузере.
Итак, на этом занятии мы с вами сделали первые шаги в понимании работы Flask и построили очень простое WSGI-приложение.
Видео по теме
Flask #1: Что это такое? Простое WSGI-приложение
Flask #2: Использование шаблонов страниц сайта
Flask #3: Контекст приложения и контекст запроса
Flask #4: Функция url_for и переменные URL-адреса
Flask #5: Подключение внешних ресурсов и работа с формами
Flask #7: Декоратор errorhandler, функции redirect и abort
Flask #8: Создание БД, установление и разрыв соединения при запросах
Flask #9: Добавление и отображение статей из БД
Flask #10: Способ представления полноценных HTML-страниц на сервере
Flask #11: Формирование ответа сервера, декораторы перехвата запроса
Flask #12: Порядок работы с cookies (куками)
Flask #13: Порядок работы с сессиями (session)
Flask #14: Регистрация пользователей и шифрование паролей
Flask #15: Авторизация пользователей на сайте через Flask-Login
Flask #16: Улучшение процесса авторизации (Flask-Login)
Flask #17: Загрузка файлов на сервер и сохранение в БД
Flask #18: Применение WTForms для работы с формами сайта
Flask #19: Обработка ошибок во Flask-WTF
Flask #23: Операции с таблицами через Flask-SQLAlchemy
© 2021 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта