В стандартной библиотеке Python есть встроенный WSGI-сервер — wsgiref. На практике чаще используются gunicorn, uWSGI и другие серверы, но функционала встроенного нам хватит для обучающих целей. В этом уроке мы научимся использовать его.
Запуск WSGI-сервера
Создадим модуль example.py. Это минимальное веб-приложение, которое будет возвращать текущее время:
from datetime import datetime
def app(environ, start_response):
time = datetime.now()
data = bytes(f"The time is {time:%H:%M:%S}", "utf-8")
start_response(
"200 OK", [("Content-Type", "text/plain"), ("Content-Length", str(len(data)))]
)
return [data]
Теперь создадим веб-сервер в модуле server.py. Импортируем из библиотеки wsgiref
функцию make_server()
. Она принимает параметрами адрес хоста, порт и приложение. Если хост указан как '', то будет использован localhost. Также функция возвращает экземпляр сервера.
Дальше нам нужно вызвать метод .serve_forever()
, чтобы сервер постоянно обслуживал запросы, пока мы принудительно не завершим его процесс:
from wsgiref.simple_server import make_server
from example import app
def server(wsgi_app):
serverd = make_server("", 8000, wsgi_app)
print("Serving HTTP on port 8000...")
serverd.serve_forever()
if __name__ == "__main__":
server(app)
Запустим веб-сервер:
python3 -m server
Serving HTTP on port 8000...
Когда сервер будет запущен, он полностью забирает управление. Вкладка терминала больше не доступна для ввода команд. В отличие от обычных скриптов, которые выполняют свою задачу и заканчиваются, веб-сервера должны слушать порт непрерывно и сразу реагировать на входящие соединения. Поэтому сервер продолжит работать, пока его не остановят.
Остановить сервер можно с помощью команды: Ctrl-C.
Такой способ запуска удобен в разработке, но в реальном окружении сервера запускают в виде демонов. Демон — процесс операционной системы, работающий в фоне.
Если в это время попытаться запустить еще один веб-сервер в соседней вкладке на том же порту, то запуск завершится с такой ошибкой:
OSError: [Errno 98] Address already in use
Такая ошибка означает, что какой-то процесс занял соответствующий порт — в данном случае 8000. В такой ситуации нужно либо остановить процесс, который мешает, либо стартовать на другом порту.
Посмотреть, какой процесс занял порт 8000, можно командой ss -ltnup 'sport = :8000'
.
После этого нужно открыть браузер и ввести http://localhost:8000
. На экран выведется текущее время. В терминале, где запущен веб-сервер, появятся записи, которые показывают входящие запросы:
127.0.0.1 - - [08/Sep/2022 14:08:39] "GET / HTTP/1.1" 200 20
127.0.0.1 - - [08/Sep/2022 14:08:44] "GET / HTTP/1.1" 200 20
127.0.0.1 - - [08/Sep/2022 14:08:45] "GET / HTTP/1.1" 200 20
127.0.0.1 - - [08/Sep/2022 14:08:45] "GET / HTTP/1.1" 200 20
Теперь снова откроем файл example.py и изменим строчку, чтобы выводить еще и сегодняшний месяц и день — data = bytes(f'The time is {time:%b %d %H:%M:%S}', 'utf-8')
. Выполните f5 в браузере, и вы увидите, что изменения не применились.
Сервер загружает модуль единожды и на каждый запрос запускает переданную в него функцию. Чтобы применились изменения, нужно перезапустить сервер. Со многими веб-фреймворками поставляется development-сервер, который работает в режиме auto-reload, то есть перезагружается при каждом изменении кода. Но в production режиме нужно перезагружать вручную.
Самостоятельная работа
- Повторите все действия из этого урока
Дополнительные материалы
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.