Django и интернет
Введение
Работая с Django мы основываемся на протоколе Http. Понимание этого протокола и работы интернета в целом это еще один важный шаг в становлении востребованного разработчика. В данном блоке мы не станем разбирать устройство работы интернета на самом низком его уровне, во-первых, потому что на момент написания этого материала я сам разбираюсь в этом достаточно плохо, во-вторых, у меня все-таки есть желание в дальнейшем с этим разобраться и посвятить этому отдельный блок. Здесь мы конкретно посвятим время разбору Http протокола и начнем мы, как и в блоке посвященному ORM, с Django Debug Toolbar.
Кэширование в Django
Почему я захотел начать именно с кэширования? Потому что в конце блока 'Django углубление' я написал, что в этом блоке мы рассмотрим некоторые вопросы работы интернета и то как они реализуются непосредственно в Django и это именно такая тема. Мы рассмотрим как реализован механизм кэширования непосредственно в Django, а в дальнейшем поговорим о кэшировании в общем виде.
Касательно кэширования в Django, почитать об этом можно на этой странице. Само кэширование в Django делится на следующие типы:
Memcached - тип кэширования полностью основанный на использовании оперативной памяти
Local-memory caching - тип кэширования используемый по умолчанию, в отличие от Memcached кэширует данные в локально памяти
Database caching - предоставляет возможность хранения кэша внутри базы данных
Filesystem caching - наиболее распространенный тип кэширования, в данном типе для каждого кэша создается отдельный файл на вашем устройстве, путь к которому прописан заранее в настройках вашего сайта
Dummy caching (for development) - создает видимость кэширования, но кэширование при этом не происходит.
Сейчас познакомимся с Filesystem caching. Кэшировать данные мы можем на уровне сайта, то есть когда весь сайт поддается механизму кэширования, это наиболее простой вариант. Второй и третий варианты - кэширование на уровне представлений и шаблонов соответственно, эти варианты нам наиболее интересны и с ними мы и разберемся.
Если кэширование это отдельная тема, то почему во введении я сказал о Django Debug Toolbar? С помощью этого инструмента мы сможем наглядно убедиться в эффективности этого механизма. И убедимся мы в этом достаточно просто, откроем любую страницу сайта и посмотрим с помощью Django Debug Toolbar на скорость работы этой страницы, после этого закэшируем данные этой страницы и посмотрим на скорость еще раз.
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': os.path.join(BASE_DIR, 'sitecache'), } }
Для начала нужно прописать настройку, в которой мы указываем путь до папки с кэшем, папку с таким названием следует создать в корневой папке проекта. Теперь мы можем пользоваться методами кэширование. Разделяют несколько видов, первый из них кэширование на уровне функций представлений, делается это добавлением декоратора cache_page, в скобках которого мы указываем время хранения кэша. Поскольку мы не используем функции представления воспользуемся вторым способом кэширования - кэширование на уровне url.
... from django.views.decorators.cache import cache_page ... urlpatterns = [ ... path('book/<slug:book_slug>', cache_page(60)(views.BookDetailView.as_view()), name='book_info'), ... ]
Реализуется это добавлением того же декоратора для того пути, который мы хотим закэшировать, первым параметром так же указывается время, вторым представление. Таким образом мы закэшировали страницу с отображением книги. Посмотрим что из этого вышло.
Перейдем на страницу любой книги. Скриншот слева страница, которую мы загружаем не используя кэш, скриншот справа страница, для загрузки которой кэш используется. Кэш страницы формируется автоматически, при первом заходе на эту страницу он создаться, после этого в течение минуты он будет использоваться. Как видите время загрузки сократилось значительно. Если теперь перейти в папку проекта в созданный ранее каталог для кэша вы увидите внутри бинарные файлы кэша.
{% extends 'book/base.html' %} {% load cache %} {% block title %}{{ title }}{% endblock %} {% block content %} {% cache 60 book_info %} <div class="mb-5"> <h2 class="text-center">{{ books.title }}</h2> <p class="text-center"><strong>{{ books.category }}</strong></p> {% for author in books.authors.all %} <h5 class="text-end">{{ author.name }}</h5> {% endfor %} <p class="px-5 mt-5 lead">{{ books.description|safe }}</p> <p class="text-end fw-bold">{{ books.year }}</p> </div> {% endcache %} {% if user.is_authenticated %} ...
Посмотрим на еще один вариант кэширования, кэширование на уровне шаблонов. Для этого используется тег {% load cache %} и далее записью {% cache (время) (имя кэша) %} {% endcache %} вы можете поместить любой фрагмент страницы в кэш. Так поместим в кэш только вывод информации о книге, а все что связано с комментариями и прочим кэшировать не станем.
Видим 5 sql запросов без кэширования и 3 с кэшированием. Так можно использовать выборочное кэширование.
Зачем может пригодиться кэшировать отдельные фрагменты, вместо всего сайта? На самом деле все достаточно очевидно. Пока для загрузки страницы информация берется из кэша, все изменения произведенные с сайтом в этот момент отображаться не будут, поэтому использование кэша будет полезно для страницах с каким-либо статическим контентом, но для динамически обновляющихся страниц, например, для комментариев, механизм кэширования не подойдет. Поэтому в редких случаях может пригодиться кэшировать весь сайт и стоит все же использовать выборочное кэширование.
Также есть низкоуровневое API кэширования, где приведены некоторые методы, которые тоже могут быть полезны, в них нет ничего сложного, разобраться с ними можно самостоятельно на странице документации, которую я оставлял выше.
Как устроен интернет
Можно теперь приступить к разбору устройства интернета, в большей степени, конечно, http протокола. Я не стану писать тут историческую хронологию становления интернета с датами и фамилиями основателей. Для работы с серверной частью нужно понимать, что происходит, когда мы вбиваем в адресную строку браузера запрос и нажимаем enter и как с этим пониманием можно взаимодействовать.
Чего мы вообще хотим добиться когда пишем какое-то веб-приложение, которым теоретически будут пользоваться люди? Самое первое чего мы хотим, это чтобы наше приложение по приобретенному для него доменному имени открывалось в браузерах пользователей. Для того чтобы это произошло данные должны быть пользователю каким то образом доставлены. Так вот эта самая доставка данных занимает в общепринятой модели взаимодействия сетей отдельный уровень и называется этот уровень - Транспортный. Это один из нескольких уровней и состоит он в свою очередь из протоколов, два самых распространенных транспортных протокола - TCP и UDP. Мы коснемся кратко и этих протоколов, но сейчас я просто хочу сформировать у вас в голове общую картинку устройства сети.
Разобрались, у нас есть транспортный уровень, и, конечно, транспортировка данных очень важна. А что еще важно?
Наверное, факт физического подключения к сети интернет, будь-то кабельное соединение или wi-fi, для этого тоже выделен свой отдельный уровень, который называется Уровень сетевого доступа. На самом деле уровень сетевого доступа можно разбить на два подуровня - Канальный и Физический. Такая разбивка происходит, если мы рассматриваем модель OSI, а если мы рассматриваем модель TCP/IP (а именно ее мы постараемся и придерживаться), то этот уровень так и останется для нас уровнем сетевого доступа, модель OSI разбита на 7 уровней, в то время как TCP/IP на 4, описывают эти модели, грубо говоря, одно и тоже, просто OSI представляет более дробный подход.
Раз уж мы начали разбираться с уровнями со стороны TCP/IP, то нам осталось познакомиться еще с двумя уровнями, что нужно помимо транспортировки данных и факта подключения устройства к сети интернет?
Для нас важно реализовать такие задачи как маршрутизация и объединение сетей основанных на разных технологиях уровня сетевого доступа в одну. За эти задачи отвечает Сетевой уровень. Маршрутизация важная и объемная тема, главный протокол, который нам в этом помогает - протокол IP. Но для нас самым важным во всей этой модели является последний четвертый уровень.
Прикладной уровень. Прикладной уровень это уже как раз протоколы, которые нам наиболее всего интересны в этом разделе, а именно HTTP, SMTP, DNS, FTP. Обо всех этих вещах мы поговорим отдельно и более детально, но перед переходом к более детальному разбору устройства интернета давайте подытожим это небольшое вступление.
OSI | TCP/IP |
---|---|
Прикладной | Прикладной |
Представлений | |
Сеансовый | |
Транспортный | Транспортный |
Сетевой | Сетевой |
Канальный | Уровень сетевого доступа |
Физический |
Устройство интернета описывают две модели OSI и TCP/IP, каждая из этих моделей поделена на уровни, выполняющие свои функции, а в выполнении этих функций уровням помогают соответствующие протоколы и технологии. К этому моменту картинка, пусть и достаточно размытая, у вас в голове уже сформирована, теперь начнем стараться делать эту картинку четче. В самом начале был сформулирован вопрос "что происходит, когда мы вбиваем в адресную строку браузера запрос и нажимаем enter?", давайте вернемся к нему.
Как работает браузер
Браузер отвечает за отображение данных запрашиваемых у сервера в понятной для человека виде. Давайте разберемся, что происходит, например, когда мы вводим в адресную строку адрес github.com, или в полном формате https://github.com, и первая и вторая запись это доменные адреса сайта. Доменный адрес такого вида понятен человек, человек лучше запомнит имена нужных ему страниц, а для браузера все как раз наоборот. Браузеру, для того чтобы понять какую страницу вернуть клиенту на его запрос, нужно узнать IP-адрес сервера, к которому относится данный url, для этой цели существует DNS протокол. Браузер обращается к DNS серверу и спрашивает у него, к какому IP-адресу относится данный url, получает ответ и возвращает его обратно устройству, с которого был отправлен запрос. После этого, зная IP-адрес, браузер отправляет запрос к серверу с таким адресом и запрашивает от него данные для нужного url, за эту часть запроса отвечает протокол http. Протокол http устанавливает соединение с сервером и начинает получать от него данные, будь-то, html, css, прочие статические файлы и таким образом перед нами в браузере появляется нужная нам интернет страница.
Один IP-адрес может быть хостом для нескольких разных доменных имен, соответственно один сервер может обсуживать несколько сайтов.
Схематично можно представить это таким образом, теперь давайте немного подробнее поговорим о DNS протоколе и доменном имени.
DNS протокол. Доменное имя
Из чего состоит домен https://github.com? На самом деле, полный адрес будет выглядеть https://www.github.com, www скрыто в адресе github, но если ввести его в таком формате мы попадем на туже стартовую страницу, любой адрес должен содержать www или его аналоги в начале доменного имени.
Обычно мы покупаем на специализированных сайтах только домен первого и второго уровня, но домен третьего уровня тоже можно приобрести и с помощью него отобразить, например, функциональную часть сайта.
Теперь что касается DNS серверов. Не существует единого DNS сервера, к которому обращаются все устройства в мире. DNS сервера представляют собой распределенную сеть и каждый из таких серверов периодически обновляет информацию о регистрации новых доменных имен, новые сайты создаются постоянно и для каждого нужен свой домен и всю эту информацию хранят DNS сервера. У интернет провайдера, которым вы пользуетесь есть свой DNS сервер. Когда вы вводите в браузере интересующее вас доменное имя вы в первую очередь обращаетесь к DNS серверу вашего провайдера. Этот DNS сервер проверяет в своей базе наличие такого доменного имени, если он его там находит, то возвращает вам его ip адрес, если нет - отправляет вас на другой DNS сервер, который теоретически может знать такой домен. Из этого следует, что DNS сервера интернет провайдеров обновляют информацию не постоянно, обычно такое обновление происходит раз в сутки, поэтому при регистрации нового домена вы увидите предупреждение, что приобретенный домен начнет работать не сразу.
У всей этой системы уровневого распределения доменного имени существует еще один 'нулевой' уровень - это корневой домен. В адресной строке он выглядит как точка в конце запроса, например, https://www.github.com. будет считаться абсолютным доменом, но обычно браузер воспринимает любой запрос без этой точки в конце, но при этом такой запрос подразумевается как абсолютный.
В итоге, полную схему получения IP-адреса сайта github можно описать таким образом.
Мы разобрались с уровнями доменного имени и схемой получения IP-адреса от DNS-сервера, но как мы уже заметили в адресе запроса, перед доменом третьего уровня записывается протокол, http или https. Этот протокол для нас наиболее интересен.
HTTP/HTTPS протокол
HTTP расшифровывается как HyperText Transfer Protocol - протокол передачи гипертекста. Любая интернет страница это html страница, к которой подключены css, javascript и какие-то статические файлы. И для того, чтобы всю эту информацию между серверами и клиентами передавать существует HTTP протокол.
Первый этап пройдем, мы получили IP-адрес для github.com, что дальше? Дальше мы обращаемся к серверу с таким адресом и просим установить с ним соединение. После успешного установления соединения мы можем начинать отправлять запросы. Весь принцип работы HTTP протокола основан на принципе взаимодействия запрос-ответ. Мы оказались с вами на главной странице github, что нам нужно для ее отображения? Все те же html, css, js и какие-то, например, иконки. Мы запросили эти файлы у сервера, он их нам в ответ прислал, и мы увидели стартовую страницу. Теперь мы решили перейти на страницу какого-то проекта и посмотреть его содержимое, мы отправляем новый запрос, который нужен для отображения этой конкретной страницы и так далее. У нас установлено соединение, отправляем запрос - получаем ответ.
Сервер может не разрешить нам установить соединение, на это может влиять несколько факторов, один из которых временные сложности с сервером и он физически недоступен. Для таких случаев у крупных ресурсов может быть выделено несколько IP-адресов для одного домена.
tsarkoilya@tsarkoilya-NBLK-WAX9X:~$ host github.com github.com has address 140.82.121.3 github.com mail is handled by 10 alt4.aspmx.l.google.com. github.com mail is handled by 10 alt3.aspmx.l.google.com. github.com mail is handled by 5 alt2.aspmx.l.google.com. github.com mail is handled by 5 alt1.aspmx.l.google.com. github.com mail is handled by 1 aspmx.l.google.com. tsarkoilya@tsarkoilya-NBLK-WAX9X:~$ host yandex.ru yandex.ru has address 5.255.255.77 yandex.ru has address 5.255.255.70 yandex.ru has address 77.88.55.60 yandex.ru has address 77.88.55.88 yandex.ru has IPv6 address 2a02:6b8:a::a yandex.ru mail is handled by 10 mx.yandex.ru.
Через терминал, с помощью команды host мы можем увидеть список IP-адресов выделенных под какой-то домен, так github расположен на одном сервере, а у яндекса есть 4 сервера и в случае недоступности одного из них пользователей можно перенаправить на другие. При том сервер формата IPv6 у яндекса всего один, а github его нет вообще.
Данные отправляются от сервера к клиенту пакетами определенного объема, отправить сразу многогигабайтные видео тяжело, перед этим их нужно разбить на эти самые пакеты и отправить видео клиенту частями, это касается не только видео, а любых данных передаваемых HTTP протоколом. Пока эти пакеты доставляются от сервера к клиенту их можно перехватить. И протокол HTTP не подразумевает шифровку этих данных, а вот как раз HTTPS подразумевает. S на конце и означает использование TLS/SSL шифрования, в этом и заключается вся разница между HTTP и HTTPS и, конечно, предпочтительней использовать именно HTTPS.
HTTP. Запросы и ответы
Когда мы зашли на главную страницу github отправили серверу GET запрос вида
GET / HTTP/1.1
Host: www.github.com
Что означает этот запрос и как они вообще формируются? Запрос начинается с глагола либо существительного, в нашем случае GET, то есть получение, далее идет путь, а после него версия протокола. На данный момент уже существует HTTP версии 2 и даже HTTP 3, пусть еще и в совсем начальном состоянии, но они только вводятся в эксплуатацию, в основном, мы по-прежнему используем HTTP 1.1 и в Django мы также работаем с этой версией протокола.
Под запросом идут заголовки запроса, среди них есть один обязательный заголовок - Host. Помните ранее мы говорили, что на одном IP-адресе могут быть расположены разные домены. Host как раз содержит в себе информацию какой именно домен из расположенных на данном сервере нас интересует. Все остальные заголовки являются необязательными. В ответ на HTTP-запрос мы получаем HTTP-ответ вида
HTTP/1.1 200 OK
Content-Length: 9745
Content-Type: text/html
Тело запроса, например html страница
Ответ также содержит версию протокола и код ответа, так 200 код означает, что запрос выполнен успешно. После статуса ответа также идут заголовки, которые могут быть абсолютно разными, так Content-Length возвращает размер отсылаемых данных в байтах, а Content-Type тип отсылаемых данных. И после идет сам запрашиваемый файл, если таковой должен быть отправлен.
HTTP-Запросы | HTTP-Ответы | ||
---|---|---|---|
GET | Получение данных | 100 - 199 | Информационные |
POST | Отправка данных | 200 - 299 | Успешные |
PUT | Изменение данных | 300 - 399 | Перенаправления |
DELETE | Удаление данных | 400 - 499 | Клиентские ошибки |
HEAD | GET запрос без тела ответа | 500 - 599 | Серверные ошибки |
Наиболее распространенные запросы это конечно GET и POST. Вернемся к github, отображение стартовой страницы GET запрос, а заполнение регистрационной формы и отправка их для регистрации это POST запрос. PUT запросы отвечают за изменение данных, например, редактирование какой-то записи в репозитории или, например, отправка комментария. Ну и DELETE удаление созданных записей.
Есть наиболее редкие типы запросов: CONNECT, OPTIONS, TRACE, PATCH. Которые позволяют преобразовать соединение запроса в TCP/IP-туннель, добавлять параметры соединения, возвращать запрос с видными в нем изменениями от промежуточных серверов и возможность изменять ресурс не перезаписывая его соответственно. Последние четыре метода используются реже, чем первые четыре, но знать об их существовании тоже не помешает.
Касательно HTTP-ответов, давайте посмотрим на наиболее распространенные
200 - успешный запрос
301 - по данному url страница недоступна, но у нее есть новый постоянный url
302 - по данному url страница недоступна, но у нее есть новый временный url
403 - страница запрещена для просмотра
404 - страница не найдена
500 - внутренняя ошибка HTTP-сервера
503 - сервер перегружен и в данный момент недоступен
В любом случае, любой из полученных ответов можно быстро найти в интернете и понять как реагировать на полученный ответ.
HTTP1.1. Заголовки
Как я упомянул выше, сейчас в основном используется версия HTTP1.1. В чем особенность этой версии? Во-первых, обязательный заголовок - Host, во-вторых, появление методов PUT, PATCH, HEAD, OPTIONS, DELETE. Этих пунктов мы так или иначе ранее коснулись. Чего мы еще не обсуждали?
Заголовок Connection. Может находиться в двух состояниях: keep-alive и close. Для чего он нужен? Connection: keep-alive означает, что у нас установлено постоянное TCP-соединение и получать все запрашиваемые данные мы можем в рамках этого одного соединения. Таким образом нам не нужно каждый раз просить по одному файла, раньше мы бы получили, к примеру, html, закрыли соединение с сервером и отправили еще один новый запрос, который должен пройти по тому же пути, для получения CSS. С keep-alive все запросы отрабатываются в рамках одного соединения, что ускоряет работу загрузки страниц. В значении Connection: close соответственно постоянное соединение установлено не будет.
Кэширование. Заголовок Cache-control. Мы уже познакомились с кэшированием в Django, но что такое кэширование в целом? Кэш это файлы нужные для отображения страницы, которые мы сохраняем на компьютере. Допустим мы зашли на кэшируемую страницу, файл с кэшем сохранился на нашем устройстве, и в следующий раз, когда мы зайдем на эту страницу мы зададим серверу вопрос 'Что-нибудь изменилось или могу использовать кэш?' и в случае положительного ответа мы обращаемся к кэшу. Этот механизм также нужен для более быстрой загрузки страниц и в целом работы интернета. Кэш хранится на устройстве пользователя, он занимает место, поэтому его нужно иногда чистить. Для управления кэшем существует заголовок Cache-control. Используются состояния
no-cache - не кэшировать
no-store - ответ не нужно хранить
private | public - кэш предназначен для конкретного пользователя | может храниться в кэше совместного использования
max-age = (значение) - срок хранения кэша в секундах
must-revalidate - проверка актуальности кэша
Это не все нововведения HTTP1.1, с остальными при желании можно ознакомиться самостоятельно.
Таким образом два основных протокола прикладного уровня мы разобрали, осталось кратенько рассказать еще о двух.
Для отправки комментария необходимо авторизоваться
Комментарии
Здесь пока ничего нет...