История о том, что было реализовано, как было измерено влияние и на какие компромиссы пришлось пойти.
Фон
Выполните поиск практически по любой теме в Google, и вы увидите мгновенно узнаваемую страницу со значимыми и релевантными результатами. Чего вы, вероятно, не осознавали, так это того, что эта страница результатов поиска в определенных сценариях обслуживается мощной веб-технологией, называемой сервисным работником .
Для развертывания поддержки сервис-воркеров для Google Поиска без негативного влияния на производительность потребовались десятки инженеров, работающих в нескольких командах. Это история о том, что было поставлено, как измерялась производительность и какие компромиссы были приняты.
Основные причины для изучения сервисных работников
Добавление сервисного работника в веб-приложение, как и любое архитектурное изменение вашего сайта, должно выполняться с четким набором целей. Команда Google Search нашла несколько ключевых причин, по которым стоит изучить добавление сервисного работника.
Ограниченное кэширование результатов поиска
Команда Google Поиска обнаружила, что пользователи часто ищут одни и те же термины несколько раз в течение короткого периода времени. Вместо того, чтобы запускать новый серверный запрос только для того, чтобы получить те же результаты, команда поиска хотела воспользоваться преимуществами кэширования и выполнять эти повторные запросы локально.
Важность свежести нельзя сбрасывать со счетов, и иногда пользователи неоднократно ищут одни и те же термины, потому что это развивающаяся тема, и они ожидают увидеть свежие результаты. Использование сервис-воркера позволяет команде поиска реализовать детальную логику для управления временем жизни локально кэшированных результатов поиска и достижения точного баланса скорости и актуальности, который, по их мнению, лучше всего подходит пользователям.
Значимый офлайн-опыт
Кроме того, команда Google Search хотела обеспечить содержательный опыт работы в автономном режиме. Когда пользователь хочет узнать о теме, он хочет сразу перейти на страницу поиска Google и начать поиск, не беспокоясь об активном подключении к Интернету.
Без сервисного работника посещение страницы поиска Google в автономном режиме привело бы просто к появлению стандартной страницы сетевых ошибок браузера, и пользователям пришлось бы не забыть вернуться и повторить попытку, как только соединение восстановится. С помощью сервис-воркера можно предоставить пользовательский автономный HTML-ответ и позволить пользователям немедленно ввести свой поисковый запрос.
Результаты не будут доступны до тех пор, пока не будет подключения к Интернету, но работник службы позволяет отложить поиск и отправить его на серверы Google, как только устройство снова подключится к сети, используя API фоновой синхронизации .
Более разумное кэширование и обслуживание JavaScript
Другой мотивацией была оптимизация кэширования и загрузки модульного кода JavaScript, который обеспечивает различные типы функций на странице результатов поиска. Объединение JavaScript дает ряд преимуществ, которые имеют смысл при отсутствии участия сервисных работников, поэтому команда поиска не хотела просто полностью прекращать объединение.
Используя способность сервисного работника создавать версии и кэшировать детализированные фрагменты JavaScript во время выполнения, группа поиска подозревала, что они могут уменьшить объем обновления кэша и обеспечить эффективное кэширование JavaScript, повторно используемого в будущем. Логика внутри их сервис-воркера может анализировать исходящий HTTP-запрос на наличие пакета, содержащего несколько модулей JavaScript, и выполнять его, объединяя несколько локально кэшированных модулей — эффективно «разделяя», когда это возможно. Это экономит пропускную способность пользователя и улучшает общую скорость реагирования.
Использование кэшированного JavaScript, обслуживаемого сервисным работником, также дает преимущества в производительности: в Chrome проанализированное представление этого JavaScript в байт-коде сохраняется и используется повторно, что приводит к меньшему количеству работы, которую необходимо выполнить во время выполнения, чтобы выполнить JavaScript на страница.
Проблемы и решения
Вот несколько препятствий, которые необходимо было преодолеть для достижения заявленных целей команды. Хотя некоторые из этих проблем специфичны для Google Search, многие из них применимы к широкому кругу сайтов, которые могут рассматривать возможность развертывания Service Worker.
Проблема: накладные расходы сервисного работника
Самой большой проблемой и единственным реальным препятствием для запуска сервис-воркера в поиске Google было обеспечение того, чтобы он не делал ничего, что могло бы увеличить задержку, воспринимаемую пользователем. Google Search очень серьезно относится к производительности и в прошлом блокировал запуск новых функций, если они приводили к увеличению задержки хотя бы на десятки миллисекунд для определенной группы пользователей.
Когда команда начала собирать данные о производительности во время своих первых экспериментов, стало очевидно, что возникнет проблема. HTML-код, возвращаемый в ответ на запросы навигации для страницы результатов поиска, является динамическим и сильно варьируется в зависимости от логики, которую необходимо запустить на веб-серверах поиска. В настоящее время у сервис-воркера нет возможности воспроизвести эту логику и немедленно вернуть кэшированный HTML — лучшее, что он может сделать, — это передать навигационные запросы на внутренние веб-серверы, что требует сетевого запроса.
Без сервис-воркера этот сетевой запрос выполняется сразу после перехода пользователя. Когда сервис-воркер зарегистрирован, его всегда необходимо запустить и дать ему возможность выполнить обработчики событий fetch
, даже если нет никаких шансов, что эти обработчики выборки будут делать что-либо, кроме обращения к сети. Количество времени, необходимое для запуска и выполнения кода сервисного работника, представляет собой чистые накладные расходы, добавляемые поверх каждой навигации:
Это ставит реализацию сервис-воркера в слишком невыгодное положение по задержке, чтобы оправдать любые другие преимущества. Кроме того, команда обнаружила, что, основываясь на измерении времени загрузки сервис-воркера на реальных устройствах, время запуска было широко распределено: некоторым мобильным устройствам начального уровня требовалось почти столько же времени для запуска сервис-воркера, сколько могло бы быть. выполните сетевой запрос HTML-кода страницы результатов.
Решение: используйте предварительную загрузку навигации.
Единственная и наиболее важная функция, которая позволила команде Google Search продвинуться вперед в запуске сервис-воркера, — это предварительная загрузка навигации . Использование предварительной загрузки навигации — ключевой выигрыш в производительности для любого сервисного работника, которому необходимо использовать ответ из сети для удовлетворения запросов навигации. Он дает браузеру подсказку о необходимости немедленно начать выполнение навигационного запроса одновременно с запуском сервис-воркера:
Пока время, необходимое для запуска сервисного работника, меньше, чем время, необходимое для получения ответа от сети, не должно быть никаких накладных расходов, вносимых сервисным работником.
Группе поиска также необходимо было избегать использования сервис-воркера на мобильных устройствах начального уровня, где время загрузки сервис-воркера могло превышать время навигационного запроса. Поскольку не существует четкого правила относительно того, что представляет собой «младшее» устройство, они придумали эвристику проверки общего объема оперативной памяти , установленной на устройстве. Все, что меньше 2 гигабайт памяти, попадало в категорию устройств нижнего уровня, где время запуска сервис-воркера было бы неприемлемым.
Доступное пространство для хранения является еще одним фактором, поскольку полный набор ресурсов, которые будут кэшироваться для будущего использования, может достигать нескольких мегабайт. Интерфейс navigator.storage
позволяет странице поиска Google заранее выяснить, подвергаются ли попытки кэширования данных риску неудачи из-за сбоев квоты хранилища.
В результате у команды поиска осталось несколько критериев, которые они могли использовать, чтобы определить, следует ли использовать сервис-воркера: если пользователь заходит на страницу поиска Google с помощью браузера, который поддерживает предварительную загрузку навигации и имеет как минимум 2 гигабайта оперативной памяти. , и достаточно свободного места для хранения, то регистрируется сервис-воркер . Браузеры или устройства, которые не соответствуют этим критериям, не попадут в сервис-воркер, но они по-прежнему будут видеть тот же интерфейс поиска Google, что и всегда.
Одним из побочных преимуществ такой выборочной регистрации является возможность выпуска меньшего по размеру и более эффективного сервис-воркера. Нацеливание на достаточно современные браузеры для запуска кода сервисного работника устраняет накладные расходы на транспиляцию и полифиллы для старых браузеров. В результате из общего размера реализации сервис-воркера было вырезано около 8 килобайт несжатого кода JavaScript.
Проблема: области сервис-воркера
Как только команда поиска провела достаточное количество экспериментов с задержками и убедилась, что использование предварительной загрузки навигации предлагает им жизнеспособный, нейтральный к задержкам путь использования сервис-воркера, некоторые практические проблемы начали выходить на передний план. Одна из этих проблем связана с правилами области действия сервис-воркера. Область действия сервис-воркера определяет, какие страницы он потенциально может контролировать.
Область действия работает на основе префикса пути URL-адреса. Для доменов, на которых размещено одно веб-приложение, это не проблема, поскольку обычно вы просто используете сервис-воркера с максимальной областью действия /
, который может получить контроль над любой страницей в домене. Но структура URL-адресов Google Search немного сложнее.
Если бы сервисному работнику была предоставлена максимальная область действия /
, он в конечном итоге смог бы получить контроль над любой страницей, размещенной на www.google.com
(или его региональном эквиваленте), и в этом домене есть URL-адреса, которые не имеют ничего общего. с помощью Google Поиска. Более разумной и ограничительной областью действия была бы /search
, которая, по крайней мере, исключила бы URL-адреса, совершенно не связанные с результатами поиска.
К сожалению, даже этот URL-путь /search
используется в различных вариантах результатов поиска Google, при этом параметры URL-запроса определяют, какой конкретный тип результатов поиска отображается. Некоторые из этих разновидностей используют совершенно другие кодовые базы, чем традиционная страница результатов веб-поиска. Например, поиск изображений и поиск покупок обслуживаются по URL-пути /search
с разными параметрами запроса, но ни один из этих интерфейсов не был готов предоставить свой собственный сервис-воркер (пока).
Решение: создать структуру диспетчеризации и маршрутизации.
Хотя есть некоторые предложения , которые позволяют использовать что-то более мощное, чем префиксы URL-путей, для определения областей действия сервис-воркера, команда Google Search застряла с развертыванием сервис-воркера, который ничего не делал для подмножества страниц, которые она контролировала.
Чтобы обойти эту проблему, команда Google Search создала специальную структуру диспетчеризации и маршрутизации, которую можно настроить для проверки таких критериев, как параметры запроса клиентской страницы, и использовать их для определения того, по какому конкретному пути кода идти. Вместо жесткого кодирования система была построена так, чтобы быть гибкой и позволять командам, которые совместно используют пространство URL-адресов, например, поиск изображений и поиск покупок, добавлять свою собственную логику сервис-воркера, если они решат ее реализовать.
Проблема: персонализированные результаты и показатели
Пользователи могут войти в Google Search, используя свои учетные записи Google, а их результаты поиска могут быть настроены на основе данных их конкретных учетных записей. Вошедшие в систему пользователи идентифицируются специальными файлами cookie браузера , которые являются почтенным и широко поддерживаемым стандартом.
Однако одним из недостатков использования файлов cookie браузера является то, что они не доступны внутри сервис-воркера, и нет возможности автоматически проверять их значения и гарантировать, что они не изменились из-за выхода пользователя из системы или переключения учетной записи. (В настоящее время предпринимаются усилия по обеспечению доступа к файлам cookie для сервисных работников , но на момент написания этой статьи этот подход является экспериментальным и широко не поддерживается.)
Несоответствие между представлением работника службы о текущем вошедшем в систему пользователе и фактическом пользователе, вошедшем в веб-интерфейс Google Поиска, может привести к неправильной персонализации результатов поиска или неправильному присвоению показателей и журналированию. Любой из этих сценариев сбоя станет серьезной проблемой для команды Google Search.
Решение: отправьте файлы cookie с помощью postMessage.
Вместо того, чтобы ждать запуска экспериментальных API и предоставления прямого доступа к файлам cookie браузера внутри сервис-воркера, команда Google Search использовала временное решение: всякий раз, когда загружается страница, контролируемая сервис-воркером, страница считывает соответствующий cookie и использует postMessage()
для отправки их сервисному работнику.
Затем сервисный работник сравнивает текущее значение файла cookie с ожидаемым значением, и в случае несоответствия он предпринимает шаги по удалению любых пользовательских данных из своего хранилища и перезагружает страницу результатов поиска без какой-либо неправильной персонализации.
Конкретные шаги, которые выполняет сервисный работник для сброса настроек до базового уровня, зависят от требований Google Search, но тот же общий подход может быть полезен другим разработчикам, которые имеют дело с персонализированными данными, отключенными от файлов cookie браузеров.
Проблема: эксперименты и динамизм
Как уже упоминалось, команда Google Search в значительной степени полагается на проведение экспериментов в рабочей среде и тестирование эффектов нового кода и функций в реальном мире, прежде чем включать их по умолчанию. Это может оказаться непростой задачей для статического сервис-воркера, который в значительной степени полагается на кэшированные данные, поскольку разрешение пользователям участвовать в экспериментах и выходить из них часто требует связи с внутренним сервером.
Решение: динамически генерируемый скрипт сервисного работника.
Решение, которое использовала команда, заключалось в использовании динамически создаваемого сценария Service Worker, настраиваемого веб-сервером для каждого отдельного пользователя, вместо одного статического сценария Service Worker, который генерируется заранее. Информация об экспериментах, которые могут повлиять на поведение сервис-воркера или сетевые запросы в целом, включена непосредственно в эти настроенные сценарии сервис-воркера. Изменение наборов активных действий для пользователя осуществляется с помощью комбинации традиционных методов, таких как файлы cookie браузера, а также предоставления обновленного кода в URL-адресе зарегистрированного сервис-воркера.
Использование динамически создаваемого сценария Service Worker также упрощает предоставление аварийного выхода в том маловероятном случае, если в реализации Service Worker имеется фатальная ошибка, которую необходимо избегать. Ответ динамического работника сервера может быть неактивной реализацией , фактически отключающей работника службы для некоторых или всех текущих пользователей.
Проблема: координация обновлений
Одна из самых сложных задач, с которыми сталкивается любое реальное развертывание сервисных работников, — это найти разумный компромисс между отказом от использования сети в пользу кэша и в то же время обеспечением того, чтобы существующие пользователи получали важные обновления и изменения вскоре после их развертывания. к производству. Правильный баланс зависит от множества факторов:
- Является ли ваше веб-приложение долгоживущим одностраничным приложением , которое пользователь оставляет открытым неопределенное время, не переходя на новые страницы.
- Какова частота развертывания обновлений на вашем внутреннем веб-сервере.
- Будет ли средний пользователь терпеть использование слегка устаревшей версии вашего веб-приложения или же свежесть является главным приоритетом.
Экспериментируя с сервис-воркерами, команда Google Search позаботилась о том, чтобы эксперименты проводились с рядом запланированных обновлений серверной части, чтобы гарантировать, что показатели и пользовательский опыт будут более точно соответствовать тому, что возвращающиеся пользователи в конечном итоге увидят в реальном мире.
Решение: сбалансировать актуальность и использование кэша.
Протестировав несколько различных вариантов конфигурации, команда Google Search обнаружила, что следующая настройка обеспечивает правильный баланс между актуальностью и использованием кэша.
URL-адрес сценария работника службы обслуживается с заголовком ответа Cache-Control: private, max-age=1500
(1500 секунд или 25 минут) и регистрируется с параметром updateViaCache, для которого установлено значение «all» , чтобы гарантировать соблюдение заголовка. Серверная часть Google Search, как вы можете себе представить, представляет собой большой, глобально распределенный набор серверов, для которых требуется как можно более 100 % бесперебойная работа. Развертывание изменения, которое может повлиять на содержимое сценария сервисного работника, выполняется поочередно.
Если пользователь обращается к серверной части, которая была обновлена, а затем быстро переходит к другой странице, которая обращается к серверной части, которая еще не получила обновленный сервис-воркер, в конечном итоге он будет переключаться между версиями несколько раз. Таким образом, указание браузеру проверять наличие обновленного сценария только в том случае, если с момента последней проверки прошло 25 минут, не имеет существенного недостатка. Преимуществом такого поведения является значительное сокращение трафика, получаемого конечной точкой, которая динамически генерирует сценарий сервисного работника.
Кроме того, в HTTP-ответе сценария сервисного работника устанавливается заголовок ETag, гарантирующий, что при проверке обновлений по истечении 25 минут сервер сможет эффективно ответить ответом HTTP 304 , если не было никаких обновлений службы. временно задействован работник.
Хотя при некоторых взаимодействиях в веб-приложении Google Search используется навигация в стиле одностраничного приложения (т. е. через History API ), по большей части Google Search — это традиционное веб-приложение, в котором используется «настоящая» навигация. Это вступило в игру, когда команда решила, что было бы эффективно использовать два варианта, ускоряющих жизненный цикл обновлений сервис-воркера: clients.claim()
и skipWaiting()
. Щелчок по интерфейсу поиска Google обычно приводит к переходу к новым HTML-документам. Вызов skipWaiting
гарантирует, что обновленный сервис-воркер получит возможность обработать новые запросы навигации сразу после установки. Аналогично, вызов clients.claim()
означает, что обновленный сервис-воркер получает возможность начать контролировать любые открытые страницы поиска Google, которые не контролируются, после активации сервис-воркера.
Подход, использованный в Google Поиске, не обязательно является решением, подходящим для всех. Он стал результатом тщательного A/B-тестирования различных комбинаций вариантов обслуживания, пока не было найдено то, что лучше всего подходит для них. Разработчики, чья серверная инфраструктура позволяет им быстрее развертывать обновления, могут предпочесть, чтобы браузер как можно чаще проверял наличие обновленного сценария Service Worker, всегда игнорируя HTTP-кеш . Если вы создаете одностраничное приложение, которое пользователи могут держать открытым в течение длительного периода времени, использование skipWaiting()
вероятно, не является для вас правильным выбором — вы рискуете столкнуться с несогласованностью кэша , если разрешите активировать новый сервис-воркер. при этом есть постоянные клиенты.
Ключевые выводы
По умолчанию сервисные работники не нейтральны к производительности.
Добавление сервисного работника в ваше веб-приложение означает вставку дополнительного фрагмента JavaScript, который необходимо загрузить и выполнить, прежде чем ваше веб-приложение получит ответы на свои запросы. Если эти ответы в конечном итоге поступают из локального кеша, а не из сети, то накладные расходы на запуск сервис-воркера обычно незначительны по сравнению с выигрышем в производительности от использования кэша в первую очередь. Но если вы знаете, что вашему сервисному работнику всегда приходится обращаться к сети при обработке навигационных запросов, использование предварительной загрузки навигации является решающим выигрышем в производительности.
Сервисные работники (по-прежнему!) являются прогрессивным усовершенствованием.
История поддержки сервисных работников сегодня гораздо ярче, чем даже год назад. Все современные браузеры теперь имеют хотя бы некоторую поддержку сервис-воркеров , но, к сожалению, есть некоторые расширенные функции сервис-воркеров, такие как фоновая синхронизация и предварительная загрузка навигации, которые не используются повсеместно. Проверка функций для конкретного подмножества функций, которые, как вы знаете, вам нужны, и регистрация сервисного работника только тогда, когда они присутствуют, по-прежнему является разумным подходом.
Аналогичным образом, если вы проводили эксперименты в реальных условиях и знаете, что низкопроизводительные устройства в конечном итоге работают плохо из-за дополнительных накладных расходов сервисного работника, вы также можете воздержаться от регистрации сервисного работника в этих сценариях.
Вы должны продолжать относиться к сервис-воркерам как к прогрессивному усовершенствованию , которое добавляется в веб-приложение, когда соблюдены все предварительные условия, и сервис-воркер добавляет что-то положительное к пользовательскому опыту и общей производительности загрузки.
Измерьте все
Единственный способ выяснить, оказало ли предоставление сервис-воркера положительное или отрицательное влияние на опыт ваших пользователей, — это экспериментировать и измерять результаты.
Особенности настройки значимых измерений зависят от того, какого поставщика аналитики вы используете, и от того, как вы обычно проводите эксперименты в настройке развертывания. Один из подходов, использующий Google Analytics для сбора показателей, подробно описан в этом тематическом исследовании на основе опыта использования сервис-воркеров в веб-приложении Google I/O.
Нецели
Хотя многие представители сообщества веб-разработчиков связывают сервисных работников с Progressive Web Apps , создание «Google Search PWA» не было первоначальной целью команды. Веб-приложение Google Search в настоящее время не предоставляет метаданные через манифест веб-приложения и не побуждает пользователей проходить процедуру добавления на главный экран . Команда поиска в настоящее время удовлетворена тем, что пользователи приходят в их веб-приложение через традиционные точки входа в поиск Google.
Вместо того, чтобы пытаться превратить веб-интерфейс Google Search в эквивалент того, что вы ожидаете от установленного приложения, основное внимание при первоначальном развертывании было направлено на постепенное улучшение существующего веб-сайта.
Благодарности
Благодарим всю команду веб-разработчиков Google Search за работу над реализацией сервис-воркера и за предоставление справочного материала, который послужил основой для написания этой статьи. Особая благодарность выражается Филиппу Голле, Раджешу Джаганнатану, Р. Сэмюэлю Клачко, Энди Мартоне, Леонардо Пенья, Рэйчел Ширер, Грегу Терроно и Клэю Вуламу.
Обновление (октябрь 2021 г.). С момента первоначальной публикации этой статьи команда Google Search переоценила преимущества и недостатки своей текущей архитектуры Service Worker. Описанный выше сервисный работник увольняется. По мере развития веб-инфраструктуры поиска Google команда может пересмотреть дизайн своего сервис-воркера.