В этом уроке мы рассмотрим, для чего нужен веб-сервер, и как устроено сетевое взаимодействие.
Процессы
Единицей исполнения в операционных системах является процесс. Это некоторая абстракция внутри ОС. Любая запущенная программа — это либо один процесс, либо набор процессов. Например, в браузерах одна вкладка, как правило, — это один процесс.
Особенность процессов в том, что они изолированы друг от друга. Например, сбой в одном процессе не влечет за собой остановку работы других. Такое свойство процессов можно наблюдать в тех ситуациях, когда одна из вкладок браузера начинает тормозить и зависает. В это время можно без проблем продолжать использовать другие вкладки.
Внутри себя процесс может делиться на потоки:
Посмотреть список процессов в Linux можно командой ps aux
либо top
.
Подробнее о менеджменте процессов можно прочитать в книгах по операционным системам.
Понимание процессов тесно связано с сетевым взаимодействием. Взаимодействие между двумя компьютерами в сети — всегда сводится к взаимодействию двух процессов. То есть нельзя подключиться к компьютеру в целом — можно подключиться только к конкретному процессу конкретной программы.
Происходит это так: одна программа, которая хочет, чтобы к ней можно было подключаться по сети, при запуске начинает слушать сетевой сокет. Такая программа называется сервером. Другая программа к ней подключается — клиент. В случае веба, сервер — это конкретный веб-сервер, например, nginx, а клиент — это браузер.
Сетевое взаимодействие между программами двух компьютеров осуществляется с помощью одного из транспортных протоколов — например TCP, поверх которого уже работает HTTP. Для обращения к другому компьютеру нужно знать два параметра: IP-адрес и порт. «Слушать сетевой сокет» — это занять определенный порт на определенном сетевом интерфейсе и дать возможность обращаться к процессу через него. По номеру порта операционная система понимает, к какому процессу пытаются обратиться.
Благодаря DNS браузер получает IP-адрес компьютера, на котором расположен сайт указанного домена. Также существует соглашение, согласно которому веб-сервер, обслуживающий сайт по протоколу HTTP, слушает порт 80
, а протокол HTTPS обслуживается на порту 443
. Поэтому браузер знает порт, на котором висит веб-сервер в ожидании входящих запросов.
Но так бывает не всегда. Во время локальной разработки обычно используются другие порты, например, 3000
или 4000
. Сам номер не принципиален. Главное, что он доступен для веб-сервера, и мы обращаемся через браузер именно к нему. Порт указывается через двоеточие после названия сайта, например, www.google.com:80
.
Веб-сервер
Веб-сервер — специализированная программа для обслуживания сайтов. Один веб-сервер может обрабатывать практически любое число сайтов (Virtual Hosts в HTTP). Он перенаправляет входящие сетевые запросы на код сайтов, получает от них ответ и возвращает его браузеру.
Еще у веб-серверов есть большое количество вспомогательных функций. Среди них кеширование, перезапись запросов, раздача статики, reverse proxy, балансировка нагрузки и многое другое.
Веб-сервера не знают, на чем написан сайт. Все способы взаимодействия веб-сервера и сайта на любом языке стандартизированы. Благодаря этому веб-серверов существует не так много, и они могут работать с сайтами, которые написаны на чем угодно.
Первым и самым простым способом взаимодействия веб-сервера с сайтом был CGI — Common Gateway Interface. Этот стандарт сразу разрабатывался с учетом того, что сервер не должен зависеть от того, на чем написан сайт. Он основан на переменных окружения.
По сути, сайт — это исполняемый файл, который запускается веб-сервером во время обработки входящего запроса и передает в него все параметры запроса через переменные окружения. Все, что требуется от скрипта, — это вернуть HTTP-ответ в стандартный вывод (STDOUT). Общий алгоритм работы выглядит так:
- Клиент запрашивает страницу сайта
- Веб-сервер принимает запрос и устанавливает переменные окружения. Через них приложению передаются данные и служебная информация
- Веб-сервер перенаправляет запросы через стандартный поток ввода (stdin) на вход вызываемой программы
- CGI-приложение выполняет все необходимые операции и формирует результаты в виде HTML
- Сформированный гипертекст возвращается веб-серверу через стандартный поток вывода (stdout). Сообщения об ошибках передаются через поток ошибок (stderr)
- Веб-сервер передает результаты запроса клиенту
WSGI
У CGI-серверов был один важный недостаток — они запускали скрипты на каждый запрос, значит, каждый раз запускался интерпретатор. В ответ CGI в Python-комьюнити предложили новый стандарт, описанный в PEP3333 — WSGI (Web Server Gateway Interface).
Основная идея WSGI — простое взаимодействие Python-приложения и веб-сервера. Согласно стандарту, веб-приложение — это просто функция или другой callable объект, которая принимает два аргумента
- Словарь переменных окружения
- Функцию, которая устанавливает параметры ответа, а возвращает iterable в качестве ответа
Минимальное WSGI-приложение, то есть приложение, которое поддерживает стандарт WSGI, выглядит примерно так:
def app(environ, start_response):
data = b"Hello, World!\n"
start_response(
"200 OK", [("Content-Type", "text/plain"), ("Content-Length", str(len(data)))]
)
return iter([data])
WSGI-сервер внутри себя вызовет функцию app
и с помощью механизма замыканий передаст в нее необходимые аргументы. Все, что касается конкретного запроса, приходит в аргументе environ
. Функция start_response()
устанавливает параметры ответа. В приведенном примере — размер ответа и тип содержимого. Затем функция просто возвращает итератор, который построчно отдает ответ.
Все современные веб-фреймворки в Python поддерживают стандарт WSGI, то есть предоставляют на верхнем уровне этот callable объект — app
во Flask, wsgi
модуль в Django. А все WSGI-серверы умеют с этим объектом работать. Некоторые веб-серверы, как nginx, не поддерживают стандарт, но все равно умеют общаться с WSGI совместимыми (gunicorn
, uWSGI
). Поэтому мы часто будем встречать классическую связку как nginx-gunicorn-Django.
Реализации
Веб-серверов довольно много. Самым популярным и эффективным решением на текущий момент является nginx. Именно с ним и стоит познакомиться. Для разработки он не понадобится, но в продакшен-среде без него нельзя.
Также набирает популярность веб-сервер Caddy, который хоть и не такой быстрый, но обладает рядом важных особенностей. Он значительно проще в настройке, из коробки умеет генерировать сертификаты и многое другое.
Еще в Python самым популярным решением для WSGI-сервера остается gunicorn. Хоть gunicorn и является полноценным веб-сервером, его чаще используют в связке с nginx или Caddy. Так он отдает все сложности по кешированию, балансировке, раздаче статики, сертификатам и многое другое на их плечи.
Самостоятельная работа
Сохраните код WSGI-приложения из урока в модуль example.py. Установите gunicorn. Всегда все устанавливайте в виртуальное окружение. Создать его можете любыми инструментами. Запустите модуль командой gunicorn -w 4 example:app
— этой командой мы запускаем WSGI-сервер gunicorn и указываем ему путь до нашей функции-приложения. Перейдите в браузер по адресу в выводе и посмотрите на результат.
Дополнительные материалы
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.