写点什么

Slack 的 Service Worker 实践:更快的启动速度与离线支持

  • 2019-10-17
  • 本文字数:3457 字

    阅读完需:约 11 分钟

Slack的Service Worker实践:更快的启动速度与离线支持

Service Worker 是网络请求的强大代理,它允许开发人员用少量 JavaScript 控制浏览器响应各个 HTTP 请求。本文将主要讲述 Slack 的 Service Worker 实践,以实现更快的启动速度与离线支持。


我们最近更新了 Slack 的桌面版,新版最大的改进一就是启动速度更快了。在本文中,我们将回顾一下提升 Slack 运行速度,从而改善用户体验的探索工作。


改进方案是从一个名为“快速启动”的原型开始的,该原型旨在(你猜对了)尽快启动 Slack。我们使用了 CDN 缓存的 HTML 文件、持久化的 Redux 存储和一个服务 Worker,从而在不到一秒钟的时间内启动客户端的精简版本(当时,使用 1-2 个工作区的用户普遍需要大约 5 秒钟的启动时间)。Service Worker 是速度提升的最大功臣,它还带来了用户亟需的脱机运行 Slack 的能力。这个原型让我们看到了桌面客户端架构彻底重构后的潜力。基于这种潜力,我们开始基于提升启动速度并引入脱机支持的核心期望重建 Slack 客户端。下面就来深入谈谈背后的工作机制。

什么是 Service Worker?

Service Worker 是网络请求的强大代理,它使开发人员可以用少量 JavaScript 控制浏览器响应各个 HTTP 请求。它们带有丰富而灵活的缓存 API,设计为将请求对象用作键,将响应对象用作值。像 Web Worker 一样,它们独立于单独运行的窗口,在自己的进程中运行。


Service Worker 是现已弃用的应用程序缓存功能的后续工具,后者是为网站提供离线功能的 API。AppCache 的工作机制是提供你要缓存下来供离线使用的静态文件清单……就是这样。它简单但不灵活,无法为开发人员提供控制权。当 W3C 编写 Service Worker 规范时非常重视之前的这些反馈,最后,Service Worker 对你的应用程序或网站进行的所有网络交互都提供了细微的控制权。


当我们第一次涉足这项技术时,支持它的浏览器只有 Chrome 而已,但是我们知道它很快就会广泛普及。现在,所有主要浏览器都开始支持 Service Worker 了。

我们如何使用 Service Worker

当你首次启动新版本的 Slack 时,我们将获取全套资产(HTML、JavaScript、CSS、字体和声音)并将其放置在 Service Worker 的缓存中。我们还将获取你的内存 Redux 存储的副本,并将其推送到 IndexedDB。下次启动时,我们检测这些缓存是否存在。如果存在,我们将使用它们来启动应用程序;如果你在线,我们将在引导后获取新数据。如果不存在,你的客户端也还是可用的。


为了区分这两种路径,我们给它们分别命名为:暖启动和冷启动。通常用户第一次启动是冷启动,没有缓存资产,也没有持久数据。暖启动时我们启动 Slack 所需要的全部数据都存储在用户本地计算机上。请注意,大多数二进制资产(图像、PDF 和视频等)均由浏览器缓存处理(并由普通的缓存头控制)。他们不需要 Service Worker 的显式处理即可离线加载。


Service Worker 生命周期

Service Worker 可以处理三个事件:安装获取激活。每个事件我们都会深入研究,但首先必须下载并注册 Service Worker。生命周期取决于浏览器处理 Service Worker 文件更新的方式。根据 MDN 的 API 文档:


当下载的文件是新文件时尝试安装——所谓新文件既可能是与现有 Service Worker 不同(按字节比较),或者是此页面 / 站点遇到的第一个 Service Worker。


每次我们更新相关的 JavaScript、CSS 或 HTML 文件时,它都会通过自定义的 webpack 插件运行,该插件会生成这些文件的带有唯一哈希值的清单。它被嵌入到 Service Worker 中,即使实现本身未更改也将在下次启动时触发更新。

安装

每当 Service Worker 更新时我们都会收到安装事件。作为响应,我们遍历嵌入式清单中的文件,获取每个文件并将它们放入共享的缓存桶中。文件使用新的缓存 API 存储,这个 API 是 Service Worker 规范的另一部分。它存储以以请求对象为键的响应对象:非常优雅直观,且与 Service Worker 事件接收请求并返回响应的方式完全一致。


我们通过部署时间为缓存桶设置键。时间戳嵌入在我们的 HTML 中,因此可以作为文件名的一部分传递给所有资产请求。分别缓存各个部署中的资产是很重要的,可以预防不匹配现象。通过这种设置,我们可以确保最初获取的 HTML 文件从缓存或网络中只能获取兼容的资产。

获取

注册后,我们的 Service Worker 将被设置为处理来自同一来源的每个网络请求。你无法选择是否让 Service Worker 处理请求,但是你完全可以控制该请求的处理方式。


首先我们检查请求。如果它在清单中并且存在于缓存中,我们将返回已缓存的响应。如果没在清单里,我们将为相同的请求返回一个获取调用,将请求传递到网络,好像不存在 Service Worker 一样。以下是我们获取处理程序的简化版本:


self.addEventListener('fetch', (e) => {  if (assetManifest.includes(e.request.url) {    e.respondWith(      caches        .open(cacheKey)        .then(cache => cache.match(e.request))        .then(response => {          if (response) return response;          return fetch(e.request);        });    );  } else {    e.respondWith(fetch(e.request));  }});
复制代码


在实际的实现中有更多 Slack 特定的逻辑,但获取处理程序的核心就是这么简单。



从 Service Worker 返回的响应将在网络检查器的大小列中标记为“(ServiceWorker)”

激活

成功安装新的 Service Worker 或更新 Service Worker 后,会触发 activate 事件。我们用它来回顾缓存资产,并让所有超过 7 天的缓存桶失效。它不仅能打扫好房间,也可以防止客户端使用过时的资产启动。

落后一个版本

你可能已经注意到,我们的实现意味着在第一次启动 Slack 客户端后,所有人都将收到上次注册 Service Worker 时获取的资产,而不是启动时最新部署的资产。我们的初始实现尝试在每次引导后更新 Service Worker。但是,典型的 Slack 客户可能只在每天早晨启动一次,并且可能会发现自己整整一天都在用落后一个版本的应用(我们每天更新多次代码)。


与你快速浏览的典型网站不一样的是,Slack 在人们工作时会在他们的计算机上停留数小时之久。这使我们的代码具有较长的保存期限,并且需要一些不一样的方法来保持最新状态。


我们仍然希望用户使用最新的版本,以便获得最新的错误修复、性能改进和功能。发布新版客户端后不久,我们就在抖动的间隔中添加了注册过程以缩小间隔。如果自上次更新以来有新版本部署,我们将获取新资产以备下次启动。如果没有,注册机制什么都不会做。做出这项更改后,客户端启动资产的平均过时时间减少了一半。



定期获取新版本,但仅使用最新版本启动

功能标记同步

功能标记是我们代码库中的条件,可让我们合并未完成的工作以准备公开发布。有了它,我们在完成功能之前很长时间就可以与应用程序的剩余部分一起自由测试功能,从而降低了风险。


Slack 的日常工作流程是发布新功能以及我们 API 中的相应更改。在引入 Service Worker 之前我们已经保证两者是同步的,但现在我们的缓存资产落后了一个版本,客户端很可能就和后端不同步了。为了解决这个问题,我们不仅要缓存 资产,还会缓存 一些 API 响应。


Service Worker 处理每个网络请求的能力简化了解决方案。每次 Service Worker 更新时,我们也会发出 API 请求,并将响应缓存在与资产相同的存储桶中。这会将我们的功能和实验与正确的资产相关联——可能它们都过时了,但一定要保持同步。


这是 Service Worker 可能引发问题的冰山一角。这个问题用 AppCache 要么完全没法解决,要么需要非常复杂的技术栈;但是有了 Service Worker 和缓存 API,解决方案就变得非常简单且顺其自然了。

总结

Service Worker 将 Slack 的资产存储在本地以备下次启动时使用,从而加快了启动速度。我们最大的延迟和可变性来源就是网络。而且如果你可以将网络排除在外,那么你也很容易提供离线支持。现在我们对脱机功能的支持非常简单——你可以启动 Slack,从以前阅读过的对话中读取消息,并将未读标记设置为在线后再同步——但有了这个舞台,我们将来就能开发更高级的离线功能了。


经过几个月的开发、试验和优化,我们已经学到了很多有关 Service Worker 如何在实践中操作的知识。而且该技术已经得到了大规模应用的实证:在公开发布后不到一个月的时间里,我们就通过数百万已安装的 Service Worker 成功地为每天数千万的请求提供了服务。与传统客户端相比,这意味着启动时间减少了约 50%;与冷启动相比启动时间缩短了约 25%。



在浏览器中使用 p50 旧版、暖启动和冷启动的测试结果(越低越好)


原文链接:


https://slack.engineering/service-workers-at-slack-our-quest-for-faster-boot-times-and-offline-support-3492cf79c88


2019-10-17 10:211993

评论

发布
暂无评论
发现更多内容

穷人版生产力工具,好用得飞起 「GitHub 热点速览」

EquatorCoco

sql git 开源

大模型之Huggingface初体验

程序员架构进阶

Transformer 大模型 7月日更 7月月更 huggingface

避免“一选定终身”,那些从就读到就业的AI真相

脑极体

AI

数字化办公需求激增,华为云桌面解锁全新云办公模式

轶天下事

内卷时代如何上云,华为云Web及移动App上云又有何亮点?

YG科技

自动化回归测试平台 AREX 0.4.0 版本发布

AREX 中文社区

开源 自动化测试 接口测试

微服务架构概览图

wiflish

微服务 微服务架构 架构治理

云会议成为企业高频服务,华为云会议为何成为众多企业的选择?

轶天下事

华为云桌面正协助企业快速进入云上数字化办公时代

轶天下事

迭代失败的4个迹象,团队中了几个?快来看看如何解决!

敏捷开发

项目管理 Scrum 迭代 开发人员

微服务架构中的单一职责原则:构建高内聚、低耦合的服务

2756

微服务 微服务架构 单一职责

与 TDengine 性能直接相关——3.0 的落盘机制优化及使用原则

爱倒腾的程序员

涛思数据 tdengine 时序数据库

红队攻防之快速打点

权说安全

网络攻防

Databend v1.2 版本发布!Data + AI

Databend

网页直播源码知识分享:“直播卫士”,查杀病毒功能在此!

山东布谷科技

软件开发 直播 源码搭建 网页开发 直播源码

安全是企业数字化的基石,华为云WeLink如何构建数字化办公护盾

轶天下事

AntDB数据库将携创新性解决方案亮相2023可信数据库发展大会

亚信AntDB数据库

数据库 AntDB AntDB数据库

视频会议进入云时代,为何企业需要华为云会议

轶天下事

从头学Java17-Modules模块

烧霞

modules java17

WebAssembly:让Istio变得更强大

谐云

istio WebAssenbly

华为云WeLink——助力企业数字化办公降本增效,提升竞争力

轶天下事

云上办公时代,华为云会议如何保障企业的开会效率与数据安全?

轶天下事

稳定性和高可用如何兼顾,华为云网站高可用解决方案满足多方案需求

轶天下事

从头学Java17-Lambda表达式

烧霞

Lambda java17

给新手产品经理的技术接口文档入门指南

Liam

产品 程序员 接口 API接口文档

企业数字化办公,华为云WeLink为何能脱颖而出

轶天下事

Slack的Service Worker实践:更快的启动速度与离线支持_大前端_Slack Engineering_InfoQ精选文章