Автор: Dali Intelligent Technology Team - Сервер HSF
введение
Протокол HTTP можно увидеть повсюду в нашей повседневной жизни.Используем ли мы компьютеры, мобильные телефоны, планшеты, браузеры или мобильные приложения, за передачей контента, который мы просматриваем, всегда стоит протокол HTTP. Среди протоколов HTTP в настоящее время наиболее широко используется протокол HTTP/1.1, но протокол HTTP/1.1 также имеет свои недостатки и недостатки.Протокол HTTP/2 провел множество оптимизаций на основе протокола HTTP/ 1.1.Официально выпущенных сервисов, поддерживающих протокол HTTP/2, становится все больше и больше. Наша энергичная команда интеллектуальных серверов провела некоторый анализ и перехват пакетов протокола HTTP/2 и его принципов.Я надеюсь, что эта статья поможет читателям получить определенное представление об основах протокола HTTP/2.
Что не так с HTTP/1.1?
Неспособность адаптироваться к постоянному увеличению количества и размера содержания интернет-страниц
После 2010 года, с бурным развитием мобильного Интернета, количество и объем ресурсов на веб-страницах значительно увеличились, но параллелизм запросов браузера под одним и тем же доменным именем ограничен, что приводит к увеличению задержек рендеринга веб-страниц.
На рисунке ниже представлена диаграмма тенденций количества и объема ресурсов на страницу в Интернете с 2011 по 2015 год.
Например, на картинке ниже результат захвата пакета новостной страницы, видно, что после завершения всех загрузок будет выдано 242 запроса, 7,2 МБ ресурсов, а общее время завершения достигает 23,77 секунды.
Почему браузеры ограничивают параллелизм запросов под одним и тем же доменным именем?
-
Для клиентских браузеров чрезмерный параллелизм включает потребление портов и накладные расходы на переключение потоков. Например, при просмотре страницы новостей, показанной выше, если 242 потока используются без ограничений для одновременного запроса ресурсов, может возникнуть дополнительная нагрузка на производительность, которую нельзя игнорировать.
-
Протокол HTTP/1.1 использует атрибут Keep Alive в протоколе TCP для поддержки мультиплексирования существующих TCP-соединений.После ожидания возврата данных с сервера TCP-соединение мультиплексируется для продолжения отправки следующего HTTP-запроса, что лучше, чем каждый запрос HTTP/1.0.Оба отключают соединение TCP намного быстрее.
-
Даже если клиент не ограничивает степень параллелизма, если все запросы отправляются на сервер одновременно, это может привести к ограничению политики управления потоком сервера.
На следующем рисунке показаны ограничения параллелизма каждого браузера:
Недостаточное использование ресурсов соединения TCP
Протокол TCP является полнодуплексным протоколом, но при том же TCP-соединении в HTTP/1.1 одновременно может быть выполнен только один HTTP-запрос, который представляет собой полудуплексную форму одного вопроса и одного ответа, что не может максимизировать использование ресурсов пропускной способности. Однако большая часть текущей сетевой среды представляет собой длинную толстую сеть, то есть сеть с большой задержкой * пропускной способностью. Мы можем представить это как две длинные и толстые водопроводные трубы.В данный момент вода, текущая в водопроводных трубах, является TCP-сообщением в полете.Идеальная ситуация состоит в том, что две водопроводные трубы непрерывно передают воду друг другу.
Огромная доля головных и общедоступных данных параметров
Отсутствие сохранения состояния HTTP/1.1 приводит к огромным заголовкам HTTP и общедоступным параметрам. Каждый запрос должен содержать много информации заголовка и общедоступных параметров. Например, в следующей таблице представлены данные запроса HTTP/1.1 для приложения. Согласно статистике, данные заголовка общественного участия составляют более 90 % всех данных запроса. Эти данные, скорее всего, останутся неизменными во время использования пользователем, и это явление распространено во многих приложениях.
| содержание | количество байтов | Доля |
|---|---|---|
| Header | 2559 | 73.6% |
| Query | 672 | 19.3% |
| Body | 245 | 7.1% |
| Всего | 3476 | 100% |
Первый опыт скорости HTTP/2
Читатели могут посетить сайт:http2.akamai.com/demo, чтобы иметь интуитивное представление об улучшении скорости HTTP/2 по сравнению с HTTP/1.1.
Видно, что в процессе запроса HTTP/1.1 браузер использует в общей сложности 6 одновременных TCP-ссылок для запроса данных изображения (в соответствии с ограничением параллелизма Chrome, равным 6, упомянутым выше), в каскаде запросов HTTP/1.1 много изображений, есть очевидная задержка ожидания в очереди.
В HTTP/2 из-за использования мультиплексирования данные передаются только по одному TCP-соединению, но скорость их передачи все равно значительно выше.
Какие улучшения сделал HTTP/2?
Процесс установления соединения HTTP/2
Как и протокол Websocket, протокол HTTP/2 основан на протоколе HTTP/1.1, и только после рукопожатия посредством обновления протокола можно официально установить соединение для отправки и получения данных. Кроме того, протокол HTTP/2 по умолчанию использует TLS для шифрования данных, а h2c указывает, что протокол открытого текста используется без TLS.
Для удобства следующего анализа перехвата пакетов мы используем Wireshark для поддержки данных протокола открытого текста h2c.nghttp2.orgСтраница захвачена, и для отправки запроса HTTP/2 используется следующая команда:
curl --http2 -v http://nghttp2.org
Читатели могут использовать этот плагин Chrome, чтобы проверить, поддерживает ли посещаемая в данный момент страница HTTP/2:chrome.Google.com/веб-магазин/….
Процесс захвата пакетов обновления протокола H2C:
- Сначала клиент отправляет пакет протокола HTTP/1.1, в котором «Соединение: обновление» в заголовке указывает, что протокол должен быть обновлен, в частности «Обновление: h2c».
- После того, как сервер получает запрос, код состояния в ответном пакете — 101 Switching Protocols, а «Upgrade: h2c» в заголовке указывает, что сервер поддерживает протокол HTTP/2 h2c. Мы будем использовать протокол HTTP/2 для передачи данные позже.
- После этого клиент отправляет на сервер фиксированное сообщение Magic, содержание которого фиксируется как: PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n, после чего обе стороны используйте HTTP/2 соответствующий протокол для связи.
сжатие заголовка
Как упоминалось в вопросе 3 HTTP/1.1, заголовок в сообщении HTTP обычно содержит множество фиксированных полей заголовка, таких как «User Agent», «Accept-Encoding» и «Accept». Эти данные заголовка часто содержат до сотен байтов или даже тысяч байтов, но тело запроса часто составляет всего несколько десятков сотен байтов. Кроме того, для пользователя большинство отправляемых им запросов имеют множество значений полей, которые каждый раз повторяются, например, во многих приложениях публичный заголовок каждого запроса принципиально не меняется, что приведет к большому количеству полосы пропускания расходуется на эти чрезвычайно повторяющиеся данные.
Поэтому HTTP/2 фокусируется на сжатии заголовков как на улучшении, но HTTP/2 не использует традиционный алгоритм сжатия строк, а разрабатывает специальный алгоритм HPACK. Зачем разрабатывать новый алгоритм? Поскольку традиционный алгоритм сжатия строк не учитывает проблему большого количества повторяющихся данных обмена данными, запрашиваемых между клиентом и сервером, если ключ Заголовка хранится путем установления двух идентичных «словарей» на обоих концах клиента и сервер -значение данных, то в следующий раз, когда вы используете данные, вам нужно будет передать только номер индекса.Кроме того, кодировка Хаффмана используется для целых чисел и строк для дальнейшего сжатия, а общая степень сжатия может достигать 50% ~ 90 %.
HPACK — это алгоритм с отслеживанием состояния, который требует, чтобы клиент и сервер поддерживали индексную таблицу каждый. HTTP/2 также единообразно преобразует имя метода, путь запроса, код состояния и т. д. в начальной строке HTTP/1.1 в форму полей заголовка, называя его: поля псевдозаголовка и добавляя его перед своим именем. «:», такие как «:method» и «:status», представляют метод запроса и код состояния соответственно.
Для наиболее часто используемых полей заголовков в HTTP/1.1 HTTP/2 определяет статическую таблицу только для чтения:HTTP за пределами G.org/specs/RFC75…, поэтому просто просмотрите таблицу, чтобы узнать имя поля и соответствующее значение. Например, индекс 3 представляет метод POST, а индекс 8 представляет код состояния 200. На следующем рисунке показан частичный снимок экрана:
...
Вы можете видеть, что определено в общей сложности 61 содержимое статической таблицы. Некоторые из определений являются определениями пары KV (например, определение № 8: статус 200), а некоторые имеют только определения ключа (например, определение № 19: принять).Для содержимого, определенного только ключом, его значение обычно не перечислимо. , и его необходимо динамически заполнять. введите.
Так что, если в статической таблице нет соответствующего значения или если это пользовательское поле? Протокол предусматривает, что для обработки этой части данных используется динамическая таблица, которая добавляется после статической таблицы и будет обновляться в словарях обеих сторон в процессе передачи обеих сторон. По мере того, как все больше и больше запросов отправляется по соединению HTTP/2, в «словаре» с обеих сторон будет все больше и больше ключей-значений, и каждое поле заголовка окончательного запроса станет одним или двумя байтами. число, данные заголовка из тысяч байтов в HTTP/1.1 могут быть представлены десятками байтов, поэтому традиционный алгоритм сжатия строк не используется.
Кроме того, строки передаются в виде несжатого открытого текста в HTTP/1.1, в то время как HTTP/2 поддерживает кодировку Хаффмана для этих строк, и используется флаг, чтобы указать, является ли это кодировкой ASCII или кодировкой Хаффмана. Учащиеся, изучавшие кодирование Хаффмана, знают, что для декодирования результата кодирования требуется таблица статического отображения.После статистики кодирования разрабатывается таблица кодирования Хаффмана:HTTP за пределами G.org/specs/RFC75…, и вот скриншот некоторых из них:
двоичный формат сообщения
HTTP/1.1 передает данные в виде простого текста, поэтому использование инструментов перехвата пакетов Wireshark и Tcpdump может быть очень удобным для перехвата пакетов и отладки. Но HTTP/2 больше не использует открытый текст и все в двоичном формате. Хотя это не удобно для человеческого чтения, это удобно для компьютерного синтаксического анализа и написания кода. HTTP/2 делит исходный заголовок и тело сообщения на несколько двоичных фреймов (фреймов) и определяет множество типов фреймов.Фрейм HEADERS используется для хранения данных заголовка, а кадр DATA используется для хранения данных тела запроса.
каркасная конструкция
Структура фрейма HTTP/2 выглядит следующим образом:
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) ...
+---------------------------------------------------------------+
- Длина: 3 байта, указывающая длину данных кадра (но не включая 9 байтов заголовка).
- Тип: 1 байт, указывающий тип последующего содержимого кадра (всего определено 10 типов кадров, которые можно условно разделить на два типа: кадры данных и кадры управления).
- Флаг: 1 байт, с разными определениями для разных типов.
- R: зарезервированный бит, в настоящее время не определенный и должен быть равен 0.
- Идентификатор потока: идентификатор потока, то есть «поток», которому принадлежит кадр (концепция потока будет объяснена позже). Получатель может использовать этот идентификатор для идентификации последовательности кадров с одинаковым идентификатором потока из исходящего потока. кадры по порядку, и следуйте последовательности.При повторной сборке реализуется виртуальный «поток».
- Полезная нагрузка кадра: полезная нагрузка кадра, соответствующая этому типу.
Наиболее распространенные кадры DATA и кадры HEADER проанализированы ниже.
Формат кадра ДАННЫХ
Структура кадра данных HTTP/2 выглядит следующим образом:
+---------------+
|Pad Length? (8)|
+---------------+-----------------------------------------------+
| Data (\*) ...
+---------------------------------------------------------------+
| Padding (\*) ...
+---------------------------------------------------------------+
- Pad Length: длина заполнения данных заполнения, ? указывает, что появление этого поля является условным, и это значение существует только тогда, когда установлен флаг PADDED.
- Данные: Данные переданы.
- Заполнение: байты заполнения без определенной семантики, и все они должны быть установлены на 0, чтобы скрыть длину сообщения.
Пример захвата пакета:
- Длина данных этого кадра составляет 62 байта.
- Тип 0, который является фреймом данных.
- В бите флага Flags: младший бит равен 1, что указывает на то, что данные потока были отправлены (эквивалентно флагу конца блока HTTP/1.1 Chunked 0\r\n\r\n), а Padded false указывает на то, что не является байтом заполнения.
- Идентификатор потока — 1, а флаг — поток 1.
- Содержание данных:
User-agent: \*
Disallow:
Sitemap: //nghttp2.org/sitemap.xml
Формат кадра ЗАГОЛОВКИ
Структура фрейма HTTP/2 HEADERS следующая:
+---------------+
|Pad Length? (8)|
+-+-------------+-----------------------------------------------+
|E| Stream Dependency? (31) |
+-+-------------+-----------------------------------------------+
| Weight? (8) |
+-+-------------+-----------------------------------------------+
| Header Block Fragment (\*) ...
+---------------------------------------------------------------+
| Padding (\*) ...
+---------------------------------------------------------------+
- Pad Length: длина заполнения данных заполнения, ? указывает, что появление этого поля является условным, и это значение существует только тогда, когда установлен флаг PADDED.
- E: Является ли зависимость потока исключительной и существует только при установленном флаге PRIORITY.
- Зависимость потока: представляет идентификатор потока, от которого зависит текущий поток, существует только при установленном флаге ПРИОРИТЕТ.
- Вес: значение веса приоритета потока (1~256).Если он существует, это означает, что флаг ПРИОРИТЕТ установлен.
- Фрагмент блока заголовка: Фрагмент блока заголовка.
- Заполнение: байты заполнения без определенной семантики, и все они должны быть установлены на 0.
Конкретный метод кодирования заголовка
Давайте подумаем, из каких данных будет состоять заголовок?
- И ключ, и значение уже могут быть закодированы с помощью индексации.
- Ключ кодируется индексом, а значение кодируется литералом.
- И ключ, и значение кодируются буквально.
Кроме того, для динамического содержимого также необходимо контролировать, вводить ли динамическую таблицу:
- Введите динамическую таблицу для последующей оптимизации передачи.
- Не вводите динамические таблицы.
- Не входите в динамическую таблицу и соглашайтесь, что заголовок никогда не войдет в динамическую таблицу.
Ниже приведен анализ захвата пакетов для двух простейших случаев:
Когда статическая таблица имеет индекс
И ключ, и значение находятся в таблице индексов (включая статическую таблицу и динамическую таблицу), а метод кодирования таков: первому биту передается 1, а остальным 7 битам передается номер индекса:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 1 | Index (7+) |
+---+---------------------------+
Пример захвата пакета:
Заголовок: :status: 200 OK Содержание кодировки: 1000 1000, что означает выражение?
- Предыдущий 1 указывает, что заголовок является KV, который уже существует в статической таблице.
- Давайте просмотрим содержимое статической таблицы перед «:status: 200 ok», ее код статической таблицы — 8, что равно 1000.
Таким образом, в сумме получается 1000 1000.
Ключ уже находится в индексной таблице, значение необходимо закодировать, передать и добавить в динамическую таблицу.
Его кодировка следующая:
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| 0 | 1 | Index (6+) |
+---+---+-----------------------+
| H | Value Length (7+) |
+---+---------------------------+
| Value String (Length octets) |
+-------------------------------+
- Первые два бита передают 01, а следующие шесть бит передают порядковый номер.
- Однобитовый H указывает, используется ли кодировка Хаффмана для последующего значения, за которым следует 7-битная длина значения.
- Ценное содержание данных.
Пример захвата пакета:
Наблюдаем его содержимое данных.Первые две цифры 01, то есть "имя находится в индексной таблице, а значение нужно закодировать и передать".Последнее содержимое 10 0001, что равно 33. 33 соответствуют в статической таблице?
Содержимое индекса 33 — это «данные», что равно 10 0001 в двоичном формате, а впереди добавляется 01, то есть 0110 0001.
После этого бит флага H того, является ли это кодировкой Хаффмана, оказывается равным 1, то есть последующее содержимое строки должно быть декодировано Хаффманом. Глядя на длину данных, отображаемую Wireshark, равную 29, следует отметить, что длина, отображаемая Wireshark, представляет собой длину после декодирования Хаффмана, а некодированная длина — это второй байт: 1001 0110, а первый бит идентифицируется как кодировка Хаффмана, длина 1 0110 или 22 байта.
Затем проверьте, являются ли последующие данные соответствующим содержимым в упомянутой выше статической таблице Хаффмана.Первый символ содержимого — S, а код Хаффмана, полученный при просмотре таблицы, соответствует 7-битному 1101110. Он точно соответствует первые 7 бит содержимого третьего байта.
Точно так же обнаруживается, что код Хаффмана следующего символа u равен 101101, что соответствует следующим данным.
виртуальный поток
HTTP/2 определяет концепцию "потока". Каждый поток имеет уникальный идентификатор в соединении HTTP/2. Поток – это двоичный двунаправленный канал передачи, и все кадры одного и того же сообщения в обоих направлениях находятся в уникальном поток, в котором передаются серии последовательных кадров данных (поскольку пакеты TCP являются последовательными, и клиент и сервер также могут обеспечить правильность последовательности при отправке сообщений), сборка этих кадров данных в последовательности представляет собой сообщение запроса и сообщение ответа в HTTP/1.1.
Таким образом, HTTP/2 может использовать потоки для одновременной передачи кадров сообщений, принадлежащих разным идентификаторам потока, по TCP-соединению.
На уровне потока сообщение представляет собой упорядоченную последовательность кадров, тогда как на уровне соединения TCP сообщение представляет собой отправленный и полученный кадр с нарушением порядка. Между несколькими ответами на запросы нет последовательной связи, поэтому в браузере не будет очереди, а также не возникнет проблема блокировки заголовка очереди, что снижает задержку и улучшает использование соединения.
Например, в следующем захвате пакетов есть 3 потока, помеченных как «0, 1, 3».
Сервер активно отправляет данные
В HTTP/1.1 сервер не может активно передавать данные клиенту, в то время как HTTP/2 поддерживает передачу данных сервером. Как показано на рисунке ниже, index.html зависит от данных some.css.В HTTP/1.1 браузеру необходимо проанализировать html и обнаружить, что он зависит от данных some.css, а затем активно отправить запрос HTTP/1.1 для получения данные. В HTTP/2, прежде чем сервер отправит index.html, он обнаружит, что он зависит от some.css, поэтому он будет активно отправлять кадр, помеченный как PROMISE, чтобы уведомить клиента о поступлении данных some.css.
Как показано на следующем рисунке: Сообщите клиенту, что ресурс CSS поступает в потоке 1, отправьте ресурс CSS в потоке 2, а поток 1 и поток 2 могут быть параллельными.
Суммировать
HTTP/2 семантически совместим с HTTP/1.1 и обновляется с HTTP/1.1 до HTTP/2 с помощью обновления протокола. HTTP/2 использует сжатие заголовков, структуру двоичного фрейма, виртуальный поток и другие методы, чтобы значительно оптимизировать производительность, поддерживая при этом функцию push сервера. Кроме того, HTTP/2 также повышает безопасность и по умолчанию требует использования протокола TLS 1.2. По данным W3Techs, по состоянию на июнь 2019 года 36,5% веб-сайтов в мире поддерживают HTTP/2.
Вакансии сервера набираются, пожалуйста, нажмите, чтобы узнать подробности JDСервер (Старший) Инженер-разработчик.