Реализуем http/2 server push с помощью nghttp2

    Всем привет, сегодня я расскажу о том, как настроил server push на своём сайте и добился увеличения скорости рендеринга страниц. Для начала о том, что же такое server push в HTTP/2. Это технология, позволяющая серверу «протолкнуть» дополнительные данные клиенту, в момент запроса основного документа. То есть в обычной ситуации запрашивает браузер html-страничку, затем обрабатывает её и приходит к выводу, что ему для корректного отображения необходимо подгрузить дополнительные файлы: стили, скрипты, изображения. После чего скачивает их и отображает конечный результат. Server push позволяет отправить дополнительные файлы уже в момент получения основного документа, и они уже будут иметься в кэше, когда они потребуются браузеру. За счёт этого возрастает скорость загрузки сайта.

    На этот раз схема будет следующая:





    Теперь непосредственно о самой реализации. В данный момент nginx в режиме HTTP/2 не поддерживает технологию server push. Для этих целей я буду использовать nghttp2 — именно его используют в CloudFlare для реализации push'ей для своих клиентов. nghttp2 — это набор инструментов, реализующих HTTP/2 протокол. А именно: standalone-сервер, клиент и обратный прокси-сервер. Нас интересует часть, реализующая прокси-сервер, программа называется nghttpx.

    Настройка


    Устанавливаем nghttp2:
    apt-get install nghttp2

    Настраиваем nghttpx. Файл конфигурации /etc/nghttpx/nghttpx.conf приводим к виду
    frontend=93.170.104.204,443 #IP и порт нашей веб-морды
    backend=127.0.0.1,6081 #IP и порт бэкенд-сервера
    private-key-file=/etc/ssl/ssl.webshake.ru.key #Закрытый ключ
    certificate-file=/etc/ssl/ssl.webshake.ru.pem #Файл с сертификатом сайта и сертификатами УЦ, аналогично nginx
    http2-proxy=no #Иначе server push будет недоступен
    workers=1 #Число воркеров

    Проверяем что сайт работает. Теперь для того, чтобы «запушить» файл стилей в браузер клиенту достаточно передать на nghttpx с бэкенда заголовок вида:
    Link: /path/to/file.css; rel=preload; as=stylesheet

    Для JS-файла заголовок будет таким:
    Link: /path/to/file.js; rel=preload; as=script

    Я добавил 14 таких заголовков, реализовав это на PHP. В файл index.php добавил строки:
    <?php
    header("link: </wp-content/themes/shootingstar/style.css>; rel=preload; as=stylesheet", false);
    header("link: </wp-content/themes/shootingstar/css/elegantfont.css?ver=4.5.3>; rel=preload; as=stylesheet", false);
    header("link: </wp-content/plugins/google-captcha/css/gglcptch.css?ver=1.23>; rel=preload; as=stylesheet", false);
    header("link: </wp-content/plugins/crayon-syntax-highlighter/css/min/crayon.min.css?ver=_2.7.2_beta>; rel=preload; as=stylesheet", false);
    header("link: </wp-content/plugins/crayon-syntax-highlighter/themes/github/github.css?ver=_2.7.2_beta>; rel=preload; as=stylesheet", false);
    header("link: </wp-includes/js/wp-emoji-release.min.js?ver=4.5.3>; rel=preload; as=script", false);
    header("link: </wp-includes/js/wp-embed.min.js?ver=4.5.3>; rel=preload; as=script", false);
    header("link: </wp-content/themes/shootingstar/js/responsive.js?ver=1.0>; rel=preload; as=script", false);
    header("link: </wp-content/themes/shootingstar/js/selectnav.js?ver=0.1>; rel=preload; as=script", false);
    header("link: </wp-content/themes/shootingstar/js/menubox.js?ver=1.0>; rel=preload; as=script", false);
    header("link: </wp-content/themes/shootingstar/js/scroll-to-top.js?ver=1.0>; rel=preload; as=script", false);
    header("link: </wp-content/themes/shootingstar/js/placeholders.js?ver=2.0.8>; rel=preload; as=script", false);
    header("link: </wp-includes/js/jquery/jquery-migrate.min.js?ver=1.4.1>; rel=preload; as=script", false);
    header("link: </wp-includes/js/jquery/jquery.js?ver=1.12.4>; rel=preload; as=script", false);

    и перезапустил Varnish для сброса всего кэша.

    Результат


    После этого в браузере загрузил главную страницу своего сайта и увидел пушнутые файлы:

    А вот так выглядит загрузка с отключенными пушами:


    Файл, который был отправлен с помощью server push имеет соответствующий заголовок.


    Вот в виде сравнения скорости рендеринга главной страницы при push'е файлов со стилями и JS и без push'ей:



    Замеры проводились инструментом WebPagetest/, о котором я узнал от своего коллеги. Проект open-source, доступна куча метрик, настроек и тестовых серверов. Всем советую!

    Также в процессе работы я накодил мини-сервис, позволяющий быстро проверить список пушаемых файлов, введя url сайта. Может быть кому пригодится — webshake.ru/post/480

    Вывод


    Технология server push'ей позволила сократить загрузку и отрисовку моего сайта с 1.7 до 1.4 секунд, а это аж 17%! Это работает и должно быть использовано.

    Напоследок еще немного метрик с WebPagetest.




    Источник: webshake.ru/post/480
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 20
    • +5
      Безусловно интересный опыт, но есть пара моментов.

      У вас в developer tools стоит «disabel cache». А без кеша смотреть на загрузку статики не серьезно.
      Так же, с учетом кеша, возникает вопрос, а стоит ли пушать при каждом запросе такое количество статики, если она у пользователя уже есть?
      Увеличение передаваемых данных скорее сделает все только хуже.
      • 0
        Наверное вы правы. Вот здесь Announcing Support for HTTP/2 Server Push как раз написано что пушить желательно некэшируемый контент, иначе пушинг может давать негативное влияние.
        • +1
          Очевидно что статья лишь пример технологии. Дальше уже бизнс логика — что пушить, когда и при каких условиях.
          Например
          — если зашли на главную и не видим сессии — однозначно пушить всё
          — если зашли на главную и видим что пользователь уже известен и заходил не давно — можно не пушить то что не поменялось
          — если зашли на главную и видим что пользователь был до обновления продакшина — пушить то что обновилось или всё, в зависимости от того какие данные в удобном виде доступны

          — если зашли не на главную, но в какой-то специализированный раздел где некоторые данные критичны — пушить
          — если зашли на страницу где есть динамически обновляемые данные, например графики — пушить графики
          И так далее и тому подобное.
          • 0
            Да, эффект в основном должен создаваться при первом посещении, нарулить это можно тучей способов — отправлять/резать заголовки, кому как нравится.
            Есть другой веб-сервер, тоже поддерживающий http/2, называется H2O. Там вообще из коробки функционал проверки наличия файлов в кэше браузера и на основе этого происходит пуш.
            • 0
              https://h2o.examp1e.net/configure/http2_directives.html#http2-casper — вот эта директива
        • 0
          Очень интересно: в nginx (который с 1.9.5 с http2 дружит, хотя модуль и экспериментальный) подобные push-ы, вроде как, можно сотворить путем отправки заголовков
          add_header 'Link' '</asset/to/push.js>; rel=preload; as=script';

          Вы же используете nghttp2 — вот в чем разница, nginx или nghttp2, если больше ничего на nghttp2 вы как бы и не настроили?
          • 0
            Да, nginx дружит, но как автор и написал, не полностью.
            Вот ананс 1.9.5 и там четко сказано:
            HTTP/2’s ‘Server Push’ feature is not supported in this release.


            Воз и ныне там вроде как.
            • 0
              Так вы бы CHANGES смотрели, что ли :) 1.9.5 не самая свежая версия на сегодня. Но поддержки или неподдержки новых фич http2 там нет, факт.

              Однако схема nghttp2 -> nginx -> apache — ох, это же «мама-не-горюй»!
        • 0
          для этого же есть простой prefetch в html
          • 0
            нет, он не для этого
            • 0
              А для чего же? В чем разница?
              • 0
                Разница в том, что предзагрузка (prefetch) файла начинается после загрузки документа html и отправки запроса на сервер, а push подразумевает последовательную загрузку html+файл без отправки запросов.
                • 0
                  Ну очевидно же что ПРЕДзагрузка, начинается «перед», а не «после», на то он и префетч. Это даже из названий понятно. Он начинает тянуть эти файлы, до рендеринга страницы, если еще и в паре с http/2.0, то не нужны самые затратные дополнтельные коннекты — практически тоже самое как пуш, но намного проще, это просто html! Да, будут дополнительные запросы, но зато это работает с любыми другими хостами, а пушить можно только те файлы что лежат на этом же сервере, что невозможно в крупном проекте.

                  P.S. 14 линков в любом случае рекомендуется собрать в 2.
                  • 0
                    Собственно даже в приведенном здесь примере видим серьезный разрыв до начала загрузки шрифтов (зачем-то аж целых 4!) с fonts.gstatic.com и как результат — они и тормозят Document Ready и далеко не на 0,3 секунды о которых написана данная статья, а их можно просто и изящно предзагрузить. Попробуйте — результат приятно удивит.
                    Пуш в данном случае — из пушки по воробьям.
                    • 0
                      что пробовать? зачем вы мне это пишете? почитайте ещё немного, разберитесь с матчастью поглубже
                      • 0
                        prefetch попробовать. Я уже давно разобрался и использую. Строчка в html явно проще чем вся эта по сути вредная статья.
                        • 0
                          разберитесь с матчастью поглубже
                          • 0
                            Для меня тут нет ничего нового, но с удовольствием выслушаю Ваши конкретные аргументы, свои я весьма развернуто изложил.
                            • 0
                              Не знаю, какие вам нужны были аргументы и в пользу чего, но хочется верить, что после ознакомления с этими текстами вам станет понятнее, как вы можете использовать пуш в своей работе:

                              https://tools.ietf.org/html/rfc7540#page-60
                              https://http2.github.io/faq/#whats-the-benefit-of-server-push
                              https://http2.github.io/faq/#how-can-i-use-http2-server-push
                              https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/#doesnt-http2-push-cover-those-same-use-cases

                              Если сравнивать между собой, то пуш будет всегда быстрее префетча, поскольку нет необходимости скачивать и парсить HTML, отправлять и обрабатывать на сервере запрос. Кроме того, prefetch имеет самый низкий приоритет при предзагрузке и не позволяет управлять порядком загрузки ресурсов. Наличие атрибута prefetch не гарантирует, что браузер немедленно отправит запрос и станет загружать ресурс:

                              Link prefetching is a browser mechanism, which utilizes browser idle time to download or prefetch documents that the user might visit in the near future.

                              Важно также понимать, что пуш не является заменой или конкурентом атрибутам предзагрузки.

                              И прошу не писать мне (вы меня с кем-то путаете!) предложения что-то пробовать, объединять какие-то непонятные файлы и всё остальное, что не имеет непосредственного отношения к моим словам в этой ветке, начиная с замечания к вашему комментарию.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.