我怎样才能删除有问题的 service worker,或者实现 "kill switch"?

How can I remove a buggy service worker, or implement a "kill switch"?

我正在我的计算机上使用 service worker API,这样我就可以了解如何在我的现实世界应用程序中从中受益。

我遇到了一个奇怪的情况,我注册了一个服务工作者,它拦截了 fetch 事件,因此它可以在向源发送请求之前检查其缓存中的请求内容。 问题是这段代码有一个错误阻止了函数发出请求,所以我的页面留空了;什么都没发生。 由于 service worker 已经注册,我第二次加载页面时它拦截了第一个请求(加载 HTML 的请求)。因为我有这个错误,那个 fetch 事件失败了,它从不请求 HTML 而我只看到它是一个空白页面。

在这种情况下,我知道删除错误服务工作者脚本的唯一方法是通过 chrome://serviceworker-internals/ 控制台。 如果此错误到达实时网站,解决它的最佳方法是什么?

谢谢!

这是一个非常糟糕的情况,希望在生产中不会发生在您身上。

在这种情况下,如果您不想浏览不同浏览器的开发者工具,chrome://serviceworker-internals/ 用于基于 blink 的浏览器,或者 about:serviceworkersabout:debugging#workers 在future) 在 Firefox 中,我想到了两件事:

  1. 使用serviceworker更新机制。您的用户代理将检查注册的工作人员是否有任何更改,将获取它并再次进入 activate 阶段。因此,您可能可以更改 serviceworker 脚本,修复(清除缓存等)任何奇怪的情况并继续工作。唯一的缺点是您需要等到浏览器更新可能需要 1 天的工作人员。
  2. 为您的工作人员添加某种终止开关。有一个特殊的 url 可以让用户访问可以恢复缓存状态等

我不确定清除浏览器数据是否会删除工作器,因此这可能是另一种选择。

对于实际情况,您需要在 byte-level 和 it will be updated in the next 24 hours 处更改服务工作者(例如,在第一行添加注释)。您可以通过单击 Update 按钮,使用 Chrome 中的 chrome://serviceworker-internals/ 模拟此操作。

即使在 service worker 本身被缓存的情况下,这也应该有效,因为更新算法的第 9 步设置了一个标志来绕过 service worker。

我还没有对此进行测试,但是 ServiceWorkerRegistration 对象上有一个 unregister() and an update() 方法。你可以从 navigator.serviceWorker.

navigator.serviceWorker.getRegistration('/').then(function(registration) {
  registration.update();
});

update 应立即检查是否有新的 serviceworker,如果有则安装它。这绕过了 24 小时的等待期,每次遇到 javascript 时都会下载 serviceworker.js。

我想在这里扩展一些其他答案,并从“将 Service Worker 部署到生产环境时我可以使用哪些策略以确保我可以进行任何需要的更改”的角度来处理这个问题?这些更改可能包括修复您在生产中发现的任何小错误,或者它可能(但希望不会)包括由于无法克服的错误而中和服务工作人员 - 所谓的“终止开关”。

为了这个答案的目的,我们假设您打电话给

navigator.serviceWorker.register('service-worker.js');

在您的页面上,这意味着您的 Service Worker JavaScript 资源是 service-worker.js。 (如果您不确定所使用的确切服务工作者 URL,请参阅下文——可能是因为您向服务工作者脚本添加了哈希或版本控制信息。

问题归结为您如何解决 service-worker.js 代码中的初始问题。如果这是一个小错误修复,那么您显然可以进行更改并将 service-worker.js 重新部署到您的托管环境。如果没有明显的错误修复,并且您不想在花时间制定解决方案时让您的用户 运行 有问题的 Service Worker 代码,最好保留一个简单,no-op service-worker.js 得心应手,像下面这样:

// A simple, no-op service worker that takes immediate control.

self.addEventListener('install', () => {
  // Skip over the "waiting" lifecycle state, to ensure that our
  // new service worker is activated immediately, even if there's
  // another tab open controlled by our older service worker code.
  self.skipWaiting();
});

/*
self.addEventListener('activate', () => {
  // Optional: Get a list of all the current open windows/tabs under
  // our service worker's control, and force them to reload.
  // This can "unbreak" any open windows/tabs as soon as the new
  // service worker activates, rather than users having to manually reload.
  self.clients.matchAll({type: 'window'}).then(windowClients => {
    windowClients.forEach(windowClient => {
      windowClient.navigate(windowClient.url);
    });
  });
});
*/

这应该是您 no-op service-worker.js 需要包含的全部内容。因为没有注册 fetch 处理程序,来自受控页面的所有导航和资源请求最终将直接针对网络,有效地为您提供相同的行为,如果根本没有 service worker 则没有。

其他步骤

如果您不打算重新部署有意义的服务工作者代码,则可以更进一步,强制删除使用 Cache Storage API, or to explicitly unregister the service worker entirely. For most common cases, that's probably going to be overkill, and following the above recommendations should be sufficient to get you in a state where your current users get the expected behavior, and you're ready to redeploy updates once you've fixed your bugs. There is some degree of overhead involved with starting up even a no-op service worker, so you can go the route of unregistering the service worker 存储的所有内容。

如果您已经处于使用 HTTP 缓存指令提供服务的 service-worker.js 的情况下,它的生命周期比您的用户可以等待的时间更长,请记住 Shift + Reload 在桌面浏览器上将强制页面在 service worker 控制之外重新加载。并非每个用户都知道如何执行此操作,而且在移动设备上也不可能。所以不要依赖 Shift + Reload 作为可行的回滚计划。

如果你不认识 service worker 怎么办URL?

以上信息假定您知道 service worker URL 是什么——service-worker.jssw.js 或其他有效不变的东西。但是,如果您在 Service Worker 脚本中包含某种版本控制或哈希信息,例如 service-worker.abcd1234.js?

会怎么样?

首先,以后尽量避免这种情况——它是against best practices。但是,如果您已经部署了多个版本化的服务工作者 URL,并且您需要为 所有 用户禁用某些功能,无论他们可能会 URL已注册,有出路

每次浏览器请求 service worker 脚本时,无论是初始注册还是更新检查,它都会设置一个名为 Service-Worker: 的 HTTP 请求 header。

假设您可以完全控制您的后端 HTTP 服务器,您可以检查传入请求是否存在此 Service-Worker: header,并始终使用您的 no-op 服务工作者脚本进行响应响应,无论请求 URL 是什么。

配置 Web 服务器以执行此操作的细节因服务器而异。

Clear-Site-Data:响应header

最后一点:当作为任何响应的一部分返回特殊的 HTTP 响应 header 时,某些浏览器会自动清除特定数据并可能注销服务工作者:Clear-Site-Data:.

设置此 header 在尝试从错误的 Service Worker 部署中恢复时会很有帮助,kill-switch 场景作为示例用例包含在该功能的 specification 中。

单独 作为 kill-switch 依赖之前,检查 browser support story 是否 Clear-Site-Data: 很重要。截至 2019 年 7 月,并非 100% 支持服务工作者的浏览器都支持它,因此目前,如果您担心从故障服务中恢复,使用 Clear-Site-Data: 和上述技术是最安全的所有浏览器中的工作人员。

您可以 'unregister' 服务工作者使用 javascript。 这是一个例子:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.getRegistrations().then(function (registrations) {
    //returns installed service workers
    if (registrations.length) {
      for(let registration of registrations) {
        registration.unregister();
      }
    }
  });
}

为了防止对其他人有帮助,我试图在浏览器中杀死 运行 的服务人员,这些服务人员访问了用于注册它们的生产站点。 我通过发布一个包含以下内容的服务-worker.js 解决了这个问题:

self.globalThis.registration.unregister();

我们已将网站从 godaddy.com 移至常规 WordPress 安装。客户(不是我们)有一个 serviceworker 文件(sw.js)缓存到他们所有的浏览器中,这完全搞砸了。我们的站点是一个普通的 WordPress 站点,没有服务人员。

它就像一个病毒,它存在于每个页面上,它不是来自我们的服务器,也没有办法轻易摆脱它。

我们在服务器的根目录下创建了一个名为 sw.js 的新空文件,然后将以下内容添加到网站上的每个页面。

<script>
if (navigator && navigator.serviceWorker && navigator.serviceWorker.getRegistration) {

    navigator.serviceWorker.getRegistration('/').then(function(registration) {
        if (registration) {
              registration.update();
              registration.unregister();
        }
});

}
</script>