Серверный Рендеринг В Бессерверной Среде

Автор материала, перевод которого мы публикуем, является одним из основателей проекта.

Вебины — бессерверная CMS на базе React, GraphQL и Node.js. Он говорит, что обслуживание многопользовательской бессерверной облачной платформы представляет собой уникальные проблемы.

Уже написано множество статей, в которых рассказывается о стандартных методах оптимизации веб-проектов.

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

Данная статья, с одной стороны, похожа на другие, а с другой – отличается от них.

Дело в том, что он посвящен оптимизации проектов, работающих в бессерверной среде.



Серверный рендеринг в бессерверной среде



Подготовка

Чтобы провести измерения, которые помогут выявить проблемы проекта, мы будем использовать webpagetest.org .

С помощью этого ресурса мы будем выполнять запросы и собирать информацию о времени выполнения различных операций.

Это позволит нам лучше понять, что видят и чувствуют пользователи при работе с проектом.

Нас особенно интересует показатель «Первый просмотр», то есть сколько времени занимает загрузка сайта у пользователя, который заходит на него впервые.

Это очень важный показатель.

Дело в том, что кеш браузера может скрывать множество узких мест в веб-проектах.



Индикаторы, отражающие характеристики загрузки сайта – выявление проблем

Взгляните на следующую диаграмму.



Серверный рендеринг в бессерверной среде

Анализ показателей старых и новых веб-проектов Здесь самым важным показателем можно считать «Time to Start Render» — время до начала рендеринга.

Если внимательно присмотреться к этому показателю, то можно увидеть, что только для того, чтобы начать рендеринг страницы, в старой версии проекта требовалось почти 2 секунды.

Причина этого кроется в самой природе одностраничных приложений (SPA).

Чтобы отобразить страницу такого приложения на экране, сначала необходимо загрузить объемный JS-связку (на следующем рисунке этот этап загрузки страницы отмечен цифрой 1).

Затем этот пакет нужно обработать в основном потоке (2).

И только после этого в окне браузера что-то может появиться.



Серверный рендеринг в бессерверной среде

(1) Загрузите пакет JS. (2) Ожидание обработки пакета в основном потоке.

Однако это лишь часть общей картины.

После того как основной поток обработает пакет JS, он делает несколько запросов к API-шлюзу.

На этом этапе обработки страницы пользователь видит вращающийся индикатор загрузки.

Это не из приятных зрелищ.

Однако пользователь еще не видел никакого содержимого страницы.

Вот раскадровка процесса загрузки страницы.



Серверный рендеринг в бессерверной среде

Загрузка страницы Все это говорит о том, что пользователь, посещающий такой сайт, не испытывает особо приятных ощущений от работы с ним.

А именно, он вынужден 2 секунды смотреть на пустую страницу, а потом еще секунду на индикатор загрузки.

Эта секунда добавляется ко времени подготовки страницы из-за того, что после загрузки и обработки JS-связки выполняются запросы к API. Эти запросы необходимы для того, чтобы загрузить данные и в результате отобразить готовую страницу.



Серверный рендеринг в бессерверной среде

Загрузка страницы Если бы проект размещался на обычном VPS, то время, необходимое для выполнения этих запросов API, было бы во многом предсказуемым.

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

В случае с облачной платформой Webiny ситуация еще хуже.

Функции AWS Lambda являются частью VPC (виртуального частного облака).

Это означает, что для каждого нового экземпляра такой функции необходимо инициализировать ENI (Elastic Network Interface).

Это существенно увеличивает время холодного запуска функций.

Ниже приведены некоторые временные показатели загрузки функций AWS Lambda внутри VPC и за его пределами.



Серверный рендеринг в бессерверной среде

Анализ нагрузки функции AWS Lambda внутри VPC и за пределами VPC (изображение взято отсюда ) Отсюда можно сделать вывод, что при запуске функции внутри VPC это дает увеличение времени холодного старта в 10 раз.

Кроме того, здесь необходимо учитывать еще один фактор – задержки передачи данных по сети.

Их продолжительность уже включена во время выполнения запросов API. Запросы инициируются браузером.

Таким образом, получается, что время, необходимое API для ответа на эти запросы, прибавляется ко времени, необходимому для того, чтобы запрос попал от браузера к API, и времени, которое необходимо для получения ответа от API. в браузер.

Эти задержки происходят во время каждого запроса.



Проблемы оптимизации

На основе приведенного анализа мы сформулировали несколько задач, которые необходимо было решить для оптимизации проекта.

Вот они:

  • Увеличьте скорость запросов API или сократите количество запросов API, блокирующих рендеринг.

  • Уменьшение размера JS бандла или перенос этого бандла на ресурсы, которые не нужны для отображения страницы.

  • Разблокировка основного потока.



Подходы к решению проблем

Вот несколько подходов к решению проблем, которые мы рассмотрели:
  1. Оптимизация кода для ускорения его выполнения.

    Этот подход требует больших усилий и стоит дорого.

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

  2. Увеличение объема оперативной памяти, доступной для функций AWS Lambda. Это легко сделать; стоимость такого решения находится где-то между средней и высокой.

    От использования этого решения можно ожидать лишь небольших положительных эффектов.

  3. Использование другого способа решения проблемы.

    Правда, на тот момент мы еще не знали, что это за метод.

В конечном итоге мы выбрали третий пункт в этом списке.

Мы рассуждали так: «А что, если нам вообще не нужны запросы к API? Что, если бы мы могли вообще обойтись без пакета JS? Это позволило бы нам решить все проблемы проекта».



Серверный рендеринг в бессерверной среде

Первая идея, которая показалась нам интересной, заключалась в создании HTML-снимка отображаемой страницы и отправке этого снимка пользователям.



Неудачная попытка

Webiny Cloud — это бессерверная инфраструктура на базе AWS Lambda, которая обеспечивает работу сайтов Webiny. Наша система умеет обнаруживать ботов.

Когда обнаруживается, что запрос был сделан ботом, этот запрос перенаправляется в экземпляр.

Кукольник , который отображает страницу с помощью Chrome без пользовательского интерфейса.

Готовый HTML-код страницы передается боту.

Это было сделано в основном из соображений SEO, из-за того, что многие боты не умеют выполнять JavaScript. Мы решили использовать тот же подход и для подготовки страниц, предназначенных для обычных пользователей.



Серверный рендеринг в бессерверной среде

Этот подход хорошо работает в средах, не поддерживающих JavaScript. Однако если вы попытаетесь передать предварительно отрендеренные страницы клиенту, браузер которого поддерживает JS, страница отрисовывается, но затем, после загрузки файлов JS, компоненты React просто не знают, куда их монтировать.

Это приводит к появлению целого ряда сообщений об ошибках в консоли.

В результате мы были недовольны этим решением.



Введение в РСБ

Сильная сторона рендеринга на стороне сервера (SSR, Server Side Rendering) заключается в том, что все запросы API выполняются внутри локальной сети.

Поскольку они обрабатываются какой-либо системой или функцией, работающей внутри VPC, для них не характерны задержки, возникающие при отправке запросов из браузера к бэкенду ресурса.

Хотя даже при таком раскладе проблема «холодного старта» остается.

Дополнительным преимуществом использования SSR является то, что мы предоставляем клиенту HTML-версию страницы, которая при работе с JS-файлами не вызовет проблем при монтировании компонентов React. И, наконец, нам не нужен очень большой пакет JS. Кроме того, мы можем обойтись без вызовов API для отображения страницы.

Пакет можно загрузить асинхронно, и это не заблокирует основной поток.

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

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



Серверный рендеринг в бессерверной среде

Производительность сайта после применения серверного рендеринга Теперь запросы к API не выполняются, и страницу можно увидеть до загрузки большого пакета JS. Но если внимательно посмотреть на первый запрос, то можно увидеть, что получение документа с сервера занимает почти 2 секунды.

Давай поговорим об этом.



Проблема с ТТФБ.

Здесь мы обсудим метрику TTFB (время до первого байта).

Вот подробности первого запроса.



Серверный рендеринг в бессерверной среде

Подробности первого запроса Чтобы обработать этот первый запрос, нам нужно сделать следующее: запустить сервер Node.js, выполнить рендеринг на стороне сервера, выполнить запросы API и выполнить JS-код, а затем вернуть конечный результат клиенту.

Проблема здесь в том, что все это в среднем занимает 1-2 секунды.

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

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

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

Разница лишь в том, что эта работа выполняется не на стороне клиента, а на сервере, в процессе серверного рендеринга.

Здесь у вас может возникнуть вопрос по поводу слова «сервер».

Все это время мы говорили о бессерверной системе.

Откуда взялся этот «сервер»? Мы, конечно, пробовали выполнять рендеринг на стороне сервера с помощью функций AWS Lambda. Но оказалось, что это очень ресурсоёмкий процесс (в частности, нужно было сильно увеличить объём памяти, чтобы получить больше ресурсов процессора).

Кроме того, сюда добавляется еще и проблема «холодного старта», о которой мы уже говорили.

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

Вернемся к последствиям использования серверного рендеринга.

Взгляните на следующую раскадровку.

Нетрудно заметить, что он особо не отличается от того, который был получен при изучении проекта, отрендеренного на клиенте.



Серверный рендеринг в бессерверной среде

Загрузка страницы при использовании серверного рендеринга Пользователь вынужден смотреть на пустую страницу в течение 2,5 секунд. Это грустно.

Хотя, глядя на эти результаты, можно подумать, что мы абсолютно ничего не добились, на самом деле это не так.

У нас был HTML-снимок страницы, содержащий все необходимое.

Этот снимок был готов к React. При этом при обработке страницы на клиенте не было необходимости делать какие-либо запросы к API. Все необходимые данные уже встроены в HTML. Единственная проблема заключалась в том, что создание этого снимка HTML заняло слишком много времени.

На этом этапе мы могли бы либо потратить больше времени на оптимизацию рендеринга на сервере, либо просто кэшировать его результаты и предоставлять клиентам снимок страницы из чего-то вроде кеша Redis. Именно это мы и сделали.



Кэширование результатов рендеринга сервера

После того, как пользователь посещает сайт Webiny, мы сначала проверяем централизованный кеш Redis, чтобы увидеть, есть ли там HTML-снимок страницы.

Если да, то мы отдаем пользователю страницу из кеша.

В среднем это снизило TTFB до 200-400 мс.

Именно после внедрения кэша мы начали замечать значительные улучшения в производительности проекта.



Серверный рендеринг в бессерверной среде

Загрузка страницы при использовании серверного рендеринга и кэша Даже пользователь, впервые посещающий сайт, видит содержимое страницы менее чем за секунду.

Давайте посмотрим, как теперь выглядит водопадная диаграмма.



Серверный рендеринг в бессерверной среде

Производительность сайта после применения серверного рендеринга и кэширования Красная линия указывает отметку времени 800 мс.

Здесь содержимое страницы загружается полностью.

Кроме того, здесь вы можете видеть, что пакеты JS загружаются примерно за 1,3 секунды.

Но это не влияет на время, необходимое пользователю для просмотра страницы.

При этом для отображения страницы не нужно делать запросы к API и загружать основной поток.

Обратите внимание, что временные индикаторы, связанные с загрузкой JS-связки, выполнением запросов к API и выполнением операций в основном потоке, по-прежнему играют важную роль в подготовке страницы к работе.

Эта инвестиция времени и ресурсов необходима для того, чтобы страница стала «интерактивной».

Но это не играет никакой роли, во-первых, для поисковых ботов, а во-вторых, для создания у пользователей ощущения «быстрой загрузки страницы».

Предположим, некая страница является «динамической».

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

После завершения рендеринга на сервере в браузер будет доставлена страница общего назначения.

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

Заголовок этой страницы изменится, чтобы отразить, что пользователь вошел в систему только после загрузки пакета JS и выполнения вызовов API. Здесь мы имеем дело с индикатором ТТИ (Time To Interactive, время до первого интерактивного взаимодействия).

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

Исправление буквально одной строчки кода привело к тому, что показатель TTFB снизился до уровня 50-90 мс.

В результате сайт теперь стал появляться в браузере примерно за 600 мс.

Однако мы столкнулись с другой проблемой.



Проблема с аннулированием кэша

«В информатике есть только две сложные вещи: аннулирование кэша и присвоение имен объектам».

Фил Карлтон Инвалидация кэша действительно является очень сложной задачей.

Как это решить? Во-первых, вы можете часто обновлять кэш, указав очень короткое время хранения для кэшированных объектов (TTL, Time To Live, время жизни).

Иногда из-за этого страницы загружаются медленнее, чем обычно.

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

В нашем случае эта проблема была решена использованием очень маленького TTL в 30 секунд. Но мы также реализовали возможность предоставлять клиентам устаревшие данные из кэша.

Пока клиенты получают такие данные, кеш обновляется в фоновом режиме.

Благодаря этому мы избавились от таких проблем, как задержка и холодный запуск, характерных для функций AWS Lambda. Вот как это работает. Пользователь посещает сайт Webiny. Проверяем HTML-кеш.

Если есть снимок страницы, мы отдаем его пользователю.

Возраст изображения может составлять даже несколько дней.

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

Выполнение этой задачи обычно занимает несколько секунд, поскольку мы создали механизм, благодаря которому у нас всегда есть несколько предварительно запущенных функций AWS Lambda, готовых к работе.

Поэтому нам не придется тратить время на функции холодного запуска при создании новых снимков.

В результате мы всегда возвращаем страницы из кэша клиентам, а когда возраст кэшированных данных достигает 30 секунд, содержимое кэша обновляется.

Кэширование — это определенно та область, которую мы еще можем улучшить.

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

Однако этот механизм обновления кэша также не идеален.

Например, предположим, что на домашней странице ресурса отображаются три последних сообщения в блоге.

Если кеш обновляется при публикации новой страницы, то с технической точки зрения после публикации будет генерироваться только кеш для этой новой страницы.

Кэш домашней страницы будет устаревшим.

Мы все еще ищем способы улучшить систему кэширования нашего проекта.

Но до сих пор упор делался на устранение существующих проблем с производительностью.

Мы считаем, что проделали достаточно хорошую работу по решению этих проблем.



Полученные результаты

Сначала мы использовали клиентский рендеринг.

Тогда в среднем пользователь мог просмотреть страницу за 3,3 секунды.

Сейчас этот показатель сократился примерно до 600 мс.

Также важно, что теперь мы обходимся без индикатора загрузки.

Такого результата нам удалось добиться в основном за счет использования серверного рендеринга.

Но без хорошей системы кэширования получается, что вычисления просто передаются с клиента на сервер.

И это приводит к тому, что время, необходимое пользователю для просмотра страницы, меняется не сильно.

Использование серверного рендеринга имеет еще одно положительное качество, о котором ранее не упоминалось.

Дело в том, что это облегчает просмотр страниц на слабых мобильных устройствах.

Скорость подготовки страницы к просмотру на таких устройствах ограничена скромными возможностями их процессоров.

Рендеринг серверов позволяет снять с них часть нагрузки.

Следует отметить, что мы не проводили никаких специальных исследований по этому вопросу, но та система, которая у нас есть, должна помочь улучшить просмотр сайта на телефонах и планшетах.

В целом можно сказать, что реализация рендеринга на стороне сервера — непростая задача.

А тот факт, что мы используем бессерверную среду, только усложняет эту задачу.

Решение наших проблем потребовало изменений кода и дополнительной инфраструктуры.

Нам нужно было создать продуманный механизм кэширования.

Но взамен мы получили много хорошего.

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

Мы думаем, что нашим пользователям это понравится.

Уважаемые читатели! Используете ли вы технологии кэширования и серверного рендеринга для оптимизации своих проектов?

Серверный рендеринг в бессерверной среде

Теги: #Оптимизация серверов #Администрирование серверов #Разработка сайтов #разработка #рендеринг сервера

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.