Эта статья написана в соавторстве с Маттео Коллина, членом технического руководящего комитета Node.js и главным архитектором @nearForm, и Джинву Ли, инженером-программистом в Google.

С момента внедрения HTTP / 2 в Node.js 8 в июле 2017 года реализация претерпела несколько этапов улучшений. Теперь мы почти готовы поднять экспериментальный флаг. Лучше всего опробовать поддержку HTTP / 2 в Node.js версии 9, в которой есть все последние исправления и улучшения.

Самый простой способ начать - использовать уровень совместимости, предоставляемый как часть нового модуля ядра http2:

Уровень совместимости предоставляет тот же высокоуровневый API (прослушиватель запросов со знакомыми объектами запроса и ответа), который предоставляет require («http»), что обеспечивает плавный начальный путь перехода на HTTP / 2.

Уровень совместимости также обеспечивает легкий путь обновления для авторов веб-фреймворков, пока и Restify, и Fastify уже поддерживают HTTP / 2 с использованием уровня совместимости Node.js HTTP / 2.

Fastify - это новый веб-фреймворк, который фокусируется на производительности без ущерба для производительности разработчиков и богатой экосистемы плагинов, которая недавно перешла на 1.0.0.

Использовать HTTP / 2 с fastify просто:

Хотя возможность запускать один и тот же код приложения поверх HTTP / 1.1 и HTTP / 2 важна для принятия протокола, уровень совместимости сам по себе не раскрывает некоторые из более мощных возможностей, доступных с HTTP / 2. Основной модуль http2 предоставляет эти дополнительные возможности через новый основной API (Http2Stream), к которому можно получить доступ через прослушиватель потока:

В Fastify доступ к Http2Stream можно получить через API request.raw.stream, например так:

HTTP / 2 Push - возможности и проблемы

HTTP / 2 дает огромное улучшение производительности по сравнению с HTTP / 1 во многих отношениях, и server push является одной из его функций для повышения производительности.

Типичный (и упрощенный) поток HTTP-запроса / ответа выглядит следующим образом (снимок экрана ниже предназначен для подключения к Hacker News):

  1. Браузер запрашивает HTML-документ.
  2. Сервер обрабатывает запрос и генерирует / отправляет HTML-документ.
  3. Браузер получает ответ и анализирует HTML-документ.
  4. Он определяет дополнительные ресурсы, которые необходимы для визуализации HTML-документа, такие как таблицы стилей, изображения, файлы JavaScript и т. Д. Он отправляет больше запросов на эти ресурсы.
  5. Сервер отвечает на каждый запрос соответствующим ресурсом.
  6. Браузер отображает страницу с использованием HTML-документа и связанных ресурсов.

Это означает, что обычно существует несколько циклов обработки запросов / ответов для визуализации одного HTML-документа, потому что с ним связаны дополнительные ресурсы, и браузеру они нужны для правильной визуализации документа. Было бы замечательно, если бы все эти связанные ресурсы можно было отправить в браузер вместе с исходным HTML-документом, а браузер не запрашивал их. И для этого нужен HTTP / 2 server push.

В HTTP / 2 сервер может проактивно проталкивать дополнительные ресурсы вместе с ответом на исходный запрос, который, по его мнению, браузер запросит позже. Позже, если браузеру они действительно нужны, он просто использует уже размещенные ресурсы вместо того, чтобы отправлять для них дополнительные запросы.

Например, предположим, что этот файл /index.html обслуживается

Сервер ответит отправкой этого файла. Но он знает, что /index.html нуждается в /static/awesome.css и /static/unicorn.png для правильного отображения. Таким образом, сервер помещает эти файлы вместе с /index.html.

На стороне клиента, как только браузер проанализирует /index.html, он определяет, что необходимы /static/awesome.css и /static/unicorn.png, но также считает, что они уже были отправлены и сохранены в кеше браузера! Таким образом, ему не нужно отправлять два дополнительных запроса, а вместо этого использовать уже отправленные ресурсы.

Пока это звучит хорошо. Но есть некоторые проблемы. Во-первых, серверу непросто узнать, какие дополнительные ресурсы могут быть добавлены для исходного запроса. Мы можем перенести это решение на уровень приложения, и разработчик должен решить. Но и разработчику понять это непросто. Один из способов сделать это - вручную проанализировать HTML и выяснить список необходимых дополнительных ресурсов, но поддерживать этот список утомительно и подвержено ошибкам при изменении приложения и обновлении файлов HTML.

Другая проблема возникает из-за того, что браузеры внутренне кэшируют ранее полученные ресурсы. Используя приведенные выше примеры файлов, если браузер вчера загрузил /index.html, он также загрузил бы /static/unicorn.png, и этот файл обычно кэшируется в браузере. Когда браузер загружает /index.html и, в свою очередь, пытается загрузить /static/unicorn.png, он знает, что последний уже кэширован, и просто использует его вместо повторного запроса. В этом случае, если сервер отправит файл /static/unicorn.png, это будет пустой тратой пропускной способности сети. У сервера должен быть способ узнать, кэширован ли ресурс в браузере.

Существуют и другие виды проблем, и Практические правила для HTTP / 2 Push хорошо документируют эти проблемы.

HTTP / 2 Auto-Push

Чтобы облегчить разработчикам Node.js поддержку функции server push, Google опубликовал пакет npm для ее автоматизации: h2-auto-push. Его цель разработки - справиться со многими проблемами, упомянутыми в разделе выше и в документе Полезные правила для HTTP / 2 Push.

Он отслеживает шаблоны запросов, поступающих от браузеров, и выясняет, какие дополнительные ресурсы связаны с первоначально запрошенным ресурсом. А позже, если запрашивается исходный ресурс, связанные ресурсы автоматически отправляются в браузер. Он также оценивает, есть ли в браузере, вероятно, уже кэшированный определенный ресурс, и пропускает отправку, если это определено.

h2-auto-push был разработан для использования промежуточным программным обеспечением для различных веб-фреймворков. Предполагается, что промежуточное ПО представляет собой промежуточное ПО для обслуживания статических файлов, и довольно легко разработать промежуточное ПО с автоматической отправкой, используя этот пакет NPM. Например, см. Fastify-auto-push. Это плагин fastify для поддержки автоматической отправки HTTP / 2, использующий пакет h2-auto-push.

Использовать это промежуточное ПО из приложения тоже довольно просто

Довольно просто, да?

Наш тест производительности показывает, что h2-auto-push дает улучшение производительности на ~ 12% по сравнению с HTTP / 2 без push и на ~ 135% по сравнению с HTTP / 1. Мы надеемся, что эта статья поможет вам лучше понять HTTP2 и преимущества, которые он может принести вашему приложению, включая HTTP2 push.

Особая благодарность Джеймсу Снелю и Дэвиду Марку Клементсу из nearForm, а также Али Шейху и Кельвину Джину из Google за помощь в редактировании этого сообщения в блоге. И большое спасибо Мэтту Лорингу из Google за его первоначальную работу над авто-пушем.