【AICon】探索RAG 技术在实际应用中遇到的挑战及应对策略!AICon精华内容已上线73%>>> 了解详情
写点什么

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:211820

评论

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

前端模拟假数据(json-server光速入门篇)

德育处主任

json 大前端 Node 28天写作 json-server

甲方日常 91

句子

工作 随笔杂谈 日常

HTML5中的拖放功能

我是哪吒

html html5 程序员 面试 大前端

微服务容错时,这些技术你要立刻想到

华为云开发者联盟

微服务 线程 服务雪崩 断路器 服务降级

Vue3 中 v-if 和 v-show 指令实现的原理 | 源码解读

五柳

源码分析 大前端 Vue3

AI、IoT、区块链、自主系统、下一代计算五大技术引领未来供应链发展

京东科技开发者

区块链 AI IoT 供应链

Volcano 监控设计解读,一看就懂

华为云开发者联盟

Kubernetes 云原生 监控 Volcano 计算

美国大选期间美股迎来大涨,舆情到底有何魔力?

星环科技

人工智能 大数据

开发质量提升系列:用户体验

罗小龙

最佳实践 方法论 28天写作

机器学习应用设计阶段的 10 个陷阱和 11 个最佳实践

浪潮云

机器学习

我们都很忙

Ian哥

28天写作

IDEA 异常退出 解决方法

任广印

IDEA

机器学习·笔记之:Cost Function - Intuition I

Nydia

GNUCash 5: 报表

lidaobing

GNUCash 28天写作

代码 or 指令,浅析ARM架构下的函数的调用过程

华为云开发者联盟

函数 任务栈 arm架构

《论雨伞道德》- 不要和自己的良心捉迷藏

石云升

读书笔记 28天写作 雨伞道德

淘宝网前期技术架构演进分析

Andy

年底跳槽之 如何找工作方向?

一笑

职业规划 28天写作

Soul 源码阅读 05|Http 长轮询同步数据分析

哼干嘛

架构师训练营知识点思维导图

晴空万里

架构师训练营第2期

个人隐私之老话重谈

张老蔫

28天写作

CSS实现数据统计

德育处主任

大前端 CSS小技巧 28天写作 纯CSS

技术根儿扎得深,不怕“首都”狂风吹!

鲁米

操作系统

工程师思维是什么?能吃吗?

Justin

工程师思维 架构设计 28天写作

区块链作用之数字货币的影响

v16629866266

数据中台:建立在数据网络效应之上的赛道

奇点云

大数据 数据中台 云原生 数据

如何养成一个好习惯

熊斌

读书笔记 28天写作

音视频行业不可或缺的功能-云端录制

anyRTC开发者

音视频 WebRTC 在线教育 直播 RTC

漫谈HTTP协议

架构精进之路

HTTP 七日更 28天写作

面对key数量多和区间查询低效问题:Hash索引趴窝,LSM树申请出场

华为云开发者联盟

数据库 数据 存储 Hash索引 LSM树

你会读书吗?

xcbeyond

读书感悟 读书方式 28天写作

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