Внести вклад
Расширенная экосистема
Service Workers и PWA

Service worker devops

Эта страница — справочник по развёртыванию и поддержке production-приложений, использующих Angular service worker. Она объясняет, как Angular service worker вписывается в более широкую production-среду, поведение service worker в различных условиях, а также доступные ресурсы и fail-safe механизмы.

Service worker и кэширование ресурсов приложения

Представьте Angular service worker как forward-кэш или край CDN, установленный в браузере конечного пользователя. Service worker отвечает на запросы Angular-приложения к ресурсам или данным из локального кэша, не дожидаясь сети. Как любой кэш, у него есть правила истечения и обновления контента.

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

В контексте Angular service worker «версия» — набор ресурсов, представляющих конкретную сборку Angular-приложения. Каждый раз, когда развёртывается новая сборка приложения, service worker считает эту сборку новой версией приложения. Это верно, даже если обновлён только один файл. В любой момент service worker может иметь в кэше несколько версий приложения и обслуживать их одновременно. Подробнее — в разделе Вкладки приложения.

Чтобы сохранить целостность приложения, 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-бандл может ссылаться на множество lazy-чанков, а имена файлов lazy-чанков уникальны для конкретной сборки приложения. Если работающее приложение версии X пытается загрузить lazy-чанк, а сервер уже обновился до версии X + 1, операция ленивой загрузки завершится ошибкой.

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

ПОЛЕЗНО: Процесс сборки создаёт файл манифеста ngsw.json на основе информации из ngsw-config.json.

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

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

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

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

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

Хешированный контент

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

Если конкретный файл не проходит проверку, Angular service worker пытается повторно загрузить контент с URL-параметром «cache-busting», чтобы предотвратить кэширование браузером или промежуточными узлами. Если этот контент тоже не проходит проверку, service worker считает всю версию приложения недействительной и прекращает её обслуживание. При необходимости service worker входит в безопасный режим, где запросы откатываются к сети. Service worker не использует свой кэш, если высок риск отдать сломанный, устаревший или недействительный контент.

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

  • Слои кэширования между origin-сервером и конечным пользователем могут отдавать устаревший контент
  • Неатомарное развёртывание может привести к тому, что Angular service worker увидит частично обновлённый контент
  • Ошибки в процессе сборки могут привести к обновлённым ресурсам без обновления ngsw.json. Возможен и обратный случай: обновлённый ngsw.json без обновлённых ресурсов.

Нехешированный контент

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

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

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

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

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

ВАЖНО: Эта гарантия сильнее, чем у обычной модели веб-развёртывания. Без service worker нет гарантии, что лениво загружаемый код той же версии, что и начальный код приложения.

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

  • Текущая версия становится недействительной из-за неудачной проверки хеша.
  • Несвязанная ошибка заставляет service worker войти в безопасный режим и временно деактивирует его.

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

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

  • Страница перезагружена/обновлена.
  • Страница запрашивает немедленную активацию обновления через сервис SwUpdate.

Обновления service worker

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

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

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

Обход service worker

В некоторых случаях может понадобиться полностью обойти service worker и позволить браузеру обработать запрос. Пример — когда вы опираетесь на возможность, пока не поддерживаемую в service workers, например отчёт о прогрессе загрузки файлов.

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

Запросы service worker, когда сервер недоступен

Service worker обрабатывает все запросы, если только service worker явно не обойдён. Service worker либо возвращает закэшированный ответ, либо отправляет запрос на сервер — в зависимости от состояния и конфигурации кэша. Service worker кэширует только ответы на немутирующие запросы, такие как GET и HEAD.

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

Отладка Angular service worker

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

Поиск и анализ отладочной информации

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

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:

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

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

Driver state: NORMAL ((nominal))

NORMAL означает, что service worker работает нормально и не находится в деградированном состоянии.

Есть два возможных деградированных состояния:

Деградированные состояния Подробности
EXISTING_CLIENTS_ONLY У service worker нет чистой копии последней известной версии приложения. Старые закэшированные версии безопасны для использования, поэтому существующие вкладки продолжают работать из кэша, но новые загрузки приложения будут обслуживаться из сети. Service worker попытается восстановиться из этого состояния, когда будет обнаружена и установлена новая версия приложения. Это происходит, когда доступен новый ngsw.json.
SAFE_MODE Service worker не может гарантировать безопасность использования закэшированных данных. Либо произошла неожиданная ошибка, либо все закэшированные версии недействительны. Весь трафик будет обслуживаться из сети с минимально возможным кодом service worker.

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

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

Хеш последнего манифеста

Latest manifest hash: eea7f5f464f90789b621170af5a569d6be077e5c

Это SHA1-хеш самой актуальной версии приложения, о которой знает service worker.

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

Last update check: never

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

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

Версия

=== Version eea7f5f464f90789b621170af5a569d6be077e5c ===

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

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

ПОЛЕЗНО: Этот хеш версии — «latest manifest hash», указанный выше. Оба клиента на последней версии. Каждый клиент перечислен по своему ID из API Clients в браузере.

Очередь idle-задач

=== Idle Task Queue ===
Last update tick: 1s496u
Last update run: never
Task queue:

- init post-load (update, cleanup)

Idle Task Queue — очередь всех отложенных задач, выполняющихся в фоне в service worker. Если в очереди есть задачи, они перечислены с описанием. В этом примере у service worker запланирована одна такая задача — post-initialization операция с проверкой обновлений и очисткой устаревших кэшей.

Счётчики last update tick/run показывают время с конкретных событий, связанных с idle-очередью. Счётчик «Last update run» показывает, когда idle-задачи фактически выполнялись в последний раз. «Last update tick» показывает время с последнего события, после которого очередь может быть обработана.

Debug log

Debug log:

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

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

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

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

  • Если смотреть в просмотрщик Cache Storage, кэш часто устаревший. Щёлкните правой кнопкой по заголовку Cache Storage и обновите кэши.

  • Остановка и запуск service worker на панели Service Worker проверяет обновления

Безопасность service worker

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

Fail-safe

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

Safety worker

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

КРИТИЧНО: Нельзя зарегистрировать этот worker напрямую, так как старые клиенты с закэшированным состоянием могут не увидеть новый index.html, устанавливающий другой скрипт worker.

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

Изменение расположения приложения

ВАЖНО: Service workers не работают за редиректом. Возможно, вы уже сталкивались с ошибкой The script resource is behind a redirect, which is disallowed.

Это может быть проблемой, если нужно изменить расположение приложения. Если настроить редирект со старого расположения, например example.com, на новое, www.example.com в этом примере, worker перестанет работать. Кроме того, редирект даже не сработает для пользователей, загружающих сайт полностью из Service Worker. Старый worker, зарегистрированный на example.com, пытается обновиться и отправляет запрос на старое расположение example.com. Этот запрос перенаправляется на новое расположение www.example.com и создаёт ошибку: The script resource is behind a redirect, which is disallowed.

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

Ещё о Angular service workers

Вас также могут заинтересовать: