Перейти к содержанию

Service worker в производстве

📅 28.02.2022

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

Предварительные условия

Базовое понимание следующего:

Рабочий сервис и кэширование ресурсов приложения

Представьте себе рабочий сервис Angular в качестве кэша пересылки или границы сети доставки контента (CDN), установленной в веб-браузере конечного пользователя. Рабочий сервис отвечает на запросы приложения Angular на ресурсы или данные из локального кэша без необходимости ждать сеть.

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

Версии приложения

В контексте Angular service worker, "версия" — это набор ресурсов, которые представляют определенную сборку приложения Angular. Всякий раз, когда развертывается новая сборка приложения, сервисный работник рассматривает эту сборку как новую версию приложения.

Это верно, даже если обновляется только один файл.

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

Для получения дополнительной информации см. раздел Вкладки приложения.

Чтобы сохранить целостность приложения, Angular service worker группирует все файлы в версии вместе. Файлы, сгруппированные в версию, обычно включают HTML, JS и CSS файлы.

Группировка этих файлов необходима для обеспечения целостности, поскольку файлы HTML, JS и CSS часто ссылаются друг на друга и зависят от конкретного содержимого.

Например, файл index.html может содержать тег <script>, который ссылается на bundle.js и может пытаться вызвать функцию startApp() из этого сценария.

Каждый раз, когда эта версия index.html будет обслуживаться, вместе с ней должен будет обслуживаться и соответствующий bundle.js.

Например, предположим, что функция startApp() переименована в runApp() в обоих файлах.

В этом сценарии не будет правильным обслуживать старый index.html, который вызывает startApp(), вместе с новым бандлом, который определяет runApp().

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

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

Идентификатор версии приложения определяется содержимым всех ресурсов, и он меняется, если любой из них меняется. На практике версия определяется по содержимому файла ngsw.json, который включает хэши для всего известного содержимого. Если какой-либо из кэшированных файлов изменяется, хэш файла изменяется в ngsw.json. Это изменение заставляет Angular service worker рассматривать активный набор файлов как новую версию.

Процесс сборки создает файл манифеста ngsw.json, используя информацию из ngsw-config.json.

Благодаря поведению версионности в Angular service worker, сервер приложений может гарантировать, что приложение Angular всегда имеет согласованный набор файлов.

Проверка обновлений

Каждый раз, когда пользователь открывает или обновляет приложение, Angular service worker проверяет наличие обновлений в приложении, ища обновления в манифесте ngsw.json. Если обновление найдено, оно автоматически загружается и кэшируется, а затем предоставляется при следующей загрузке приложения.

Целостность ресурсов

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

Рабочий сервис игнорирует такие ограничения и эффективно кэширует все приложение.

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

Хешированное содержимое

Чтобы обеспечить целостность ресурсов, рабочий сервис Angular проверяет хэши всех ресурсов, для которых у него есть хэш. Для приложения, созданного с помощью Angular CLI, это все, что находится в директории dist, покрытой пользовательской конфигурацией src/ngsw-config.json.

Если определенный файл не прошел проверку, рабочий сервис Angular пытается повторно получить содержимое, используя параметр URL "cache-busting" для предотвращения кэширования браузером или промежуточным кэшем. Если это содержимое также не проходит валидацию, работник сервиса считает всю версию приложения недействительной и прекращает обслуживание приложения.

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

Несовпадения хэшей могут возникать по разным причинам:

  • Кэширующие слои между сервером происхождения и конечным пользователем могут обслуживать несвежий контент

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

  • Ошибки в процессе сборки могут привести к обновлению ресурсов без обновления ngsw.json.

    Может произойти и обратная ситуация, в результате которой обновленный ngsw.json не содержит обновленных ресурсов.

Нехешированное содержимое

Единственные ресурсы, которые имеют хэши в манифесте ngsw.json — это ресурсы, которые присутствовали в каталоге dist на момент создания манифеста. Другие ресурсы, особенно загруженные из CDN, имеют содержимое, которое неизвестно на момент сборки или обновляется чаще, чем развертывается приложение.

Если работник службы Angular не имеет хэша для проверки достоверности ресурса, он все равно кэширует его содержимое. В то же время он соблюдает заголовки кэширования HTTP, используя политику stale while revalidate. Angular service worker продолжает обслуживать ресурс даже после того, как заголовки кэширования HTTP указывают на то.

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

Таким образом, неработающие нехешированные ресурсы не остаются в кэше сверх заданного времени жизни.

Вкладки приложения

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

Angular service worker предоставляет гарантию: запущенное приложение продолжает работать в той же версии. Если другой экземпляр приложения открывается в новой вкладке браузера, то обслуживается самая актуальная версия приложения.

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

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

Без сервисного работника нет гарантии, что лениво загруженный код относится к той же версии, что и исходный код приложения.

Рабочий сервис Angular может изменить версию запущенного приложения при возникновении таких ошибок, как:

  • Текущая версия становится недействительной из-за неудачного хэша.

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

Angular service worker очищает версии приложений, когда ни одна вкладка не использует их.

Другими причинами, по которым работник службы Angular может изменить версию запущенного приложения, являются обычные события:

  • Страница перезагружается/обновляется

  • Страница запрашивает обновление, которое должно быть немедленно активировано с помощью сервиса SwUpdate.

Обновления рабочего сервиса

Angular service worker — это небольшой скрипт, который запускается в веб-браузерах. Время от времени рабочий сервис обновляется с исправлениями ошибок и улучшениями функций.

Angular service worker загружается при первом открытии приложения и при обращении к приложению после периода бездействия. Если работник службы изменяется, он обновляется в фоновом режиме.

Большинство обновлений работника службы Angular прозрачны для приложения. Старые кэши остаются в силе, а содержимое обслуживается нормально. Иногда исправление ошибки или функция в Angular service worker может потребовать аннулирования старых кэшей.

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

Обход работника службы

В некоторых случаях вы можете захотеть полностью обойти сервисный работник и позволить браузеру обрабатывать запрос. Например, если вы полагаетесь на функцию, которая в настоящее время не поддерживается рабочими службами, такую как report progress on uploaded files.

Чтобы обойти сервисный работник, задайте ngsw-bypass в заголовке запроса или в параметре запроса. Значение заголовка или параметра запроса игнорируется и может быть пустым или опущенным.

Запросы сервисного работника, когда сервер не может быть достигнут

Сервисный работник обрабатывает все запросы, если сервисный работник явно не обойден. В зависимости от состояния и конфигурации кэша работник службы либо возвращает кэшированный ответ, либо отправляет запрос на сервер. Рабочий сервис кэширует только ответы на некоммутативные запросы, такие как GET и HEAD.

Если работник службы получает ошибку от сервера или не получает ответа, он возвращает статус ошибки, который указывает на результат вызова. Например, если работник службы не получает ответа, он создает статус 504 Gateway Timeout для возврата. Статус 504 в этом примере может быть возвращен потому, что сервер находится в автономном режиме или клиент отключен.

Отладка Angular service worker

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

Нахождение и анализ отладочной информации

Angular service worker предоставляет отладочную информацию в виртуальном каталоге ngsw/. В настоящее время единственным доступным URL является ngsw/state.

Вот пример содержимого этой страницы отладки:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
NGSW Debug Info:

Driver version: 13.3.7
Driver state: NORMAL ((nominal))
Latest manifest hash: eea7f5f464f90789b621170af5a569d6be077e5c
Last update check: never

=== Version eea7f5f464f90789b621170af5a569d6be077e5c ===

Clients: 7b79a015-69af-4d3d-9ae6-95ba90c79486, 5bc08295-aaf2-42f3-a4cc-9e4ef9100f65

=== Idle Task Queue ===
Last update tick: 1s496u
Last update run: never
Task queue:
 * init post-load (update, cleanup)

Debug log:

Состояние драйвера

В первой строке указано состояние драйвера:

1
Driver state: NORMAL ((nominal))

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

Существует два возможных состояния деградации:

Degraded states Details
EXISTING_CLIENTS_ONLY У работника службы нет чистой копии последней известной версии приложения. Более старые кэшированные версии безопасны для использования, поэтому существующие вкладки продолжают работать из кэша, но новые загрузки приложения будут обслуживаться из сети. Рабочий сервис попытается восстановиться из этого состояния, когда будет обнаружена и установлена новая версия приложения. Это произойдет, когда будет доступен новый ngsw.json.
SAFE_MODE Рабочий сервис не может гарантировать безопасность использования кэшированных данных. Либо произошла непредвиденная ошибка, либо все кэшированные версии недействительны. Весь трафик будет обслуживаться из сети, выполняя как можно меньше кода сервисного работника.

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

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

Новый экземпляр запускается в режиме NORMAL, независимо от состояния предыдущего экземпляра.

Последний хэш манифеста

1
Latest manifest hash: eea7f5f464f90789b621170af5a569d6be077e5c

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

Проверка последнего обновления

1
Last update check: never

Указывает последний раз, когда работник службы проверял наличие новой версии, или обновления, приложения. never указывает, что работник службы никогда не проверял наличие обновления.

В этом примере отладочного файла проверка обновлений в настоящее время запланирована, как объясняется в следующем разделе.

Версия

1
2
3
=== Version eea7f5f464f90789b621170af5a569d6be077e5c ===

Clients: 7b79a015-69af-4d3d-9ae6-95ba90c79486, 5bc08295-aaf2-42f3-a4cc-9e4ef9100f65

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

Этот хэш версии является "хэшем последней версии манифеста", перечисленным выше. Оба клиента находятся на последней версии.

Каждый клиент указан по его ID из API Clients в браузере.

Очередь неработающих задач

1
2
3
4
5
=== Idle Task Queue ===
Last update tick: 1s496u
Last update run: never
Task queue:
 * init post-load (update, cleanup)

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

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

Счетчики тиков/прогонов последнего обновления показывают время, прошедшее с момента определенных событий, связанных с простаивающей очередью. Счетчик "Last update run" показывает время последнего выполнения неработающих задач.

Счетчик "Last update tick" показывает время с момента последнего события, после которого очередь могла быть обработана.

Отладочный журнал

1
Debug log:

Ошибки, возникающие в работнике службы, регистрируются здесь.

Инструменты разработчика

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

  • При использовании инструментов разработчика работник службы работает в фоновом режиме и никогда не перезапускается.

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

  • Если вы посмотрите в окне просмотра хранилища кэша, кэш часто бывает устаревшим.

    Щелкните правой кнопкой мыши заголовок Cache Storage и обновите кэш.

  • Остановка и запуск работника службы в панели Service Worker проверяет наличие обновлений.

Безопасность работника службы

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

Fail-safe

Чтобы деактивировать сервисный работник, переименуйте файл ngsw.json или удалите его. Когда запрос к файлу ngsw.json возвращает 404, рабочий удаляет все свои кэши и снимает себя с регистрации, по сути самоуничтожаясь.

Работник службы безопасности

Небольшой скрипт safety-worker.js также включен в NPM-пакет @angular/service-worker. При загрузке он снимает свою регистрацию в браузере и удаляет кэши рабочих служб.

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

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

Вместо этого, вы должны передать содержимое файла safety-worker.js по URL скрипта Service Worker, который вы пытаетесь снять с регистрации. Вы должны продолжать делать это до тех пор, пока не убедитесь, что все пользователи успешно отменили регистрацию старого рабочего. Для большинства сайтов это означает, что вы должны обслуживать безопасный рабочий по старому URL Service Worker навсегда.

Этот скрипт можно использовать для деактивации @angular/service-worker и удаления соответствующих кэшей. Он также удаляет любые другие Service Worker, которые могли обслуживаться в прошлом на вашем сайте.

Изменение местоположения вашего приложения

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

Это может стать проблемой, если вам нужно изменить местоположение приложения. Если вы установите перенаправление со старого местоположения, например, example.com, на новое, www.example.com в данном примере, рабочий перестанет работать.

Кроме того, перенаправление не сработает даже для пользователей, которые загружают сайт полностью с Service Worker.

Старый рабочий, который был зарегистрирован на example.com, пытается обновиться и посылает запрос на старое местоположение example.com. Этот запрос перенаправляется на новое место www.example.com и создает ошибку: Ресурс скрипта находится за перенаправлением, которое запрещено.

Чтобы исправить ситуацию, вам может потребоваться деактивировать старый рабочий, используя одну из предыдущих техник: Fail-safe или Safety Worker.

Подробнее о рабочих службах Angular

Вам также может быть интересно следующее:

Комментарии