一文解密 Netflix 的快速事件通知系统是如何工作的
Netflix 拥有超过 2.2 亿活跃会员,他们会使用各种功能执行大量操作。近乎实时地对这些操作做出反应以保持跨设备的体验一致,这对于确保最佳会员体验至关重要。考虑到支持的设备种类繁多以及会员执行的操作数量之多,这不是一件容易的事。为此,我们开发了一个快速事件通知系统 (RENO),以支持需要服务器的可扩展性和并以可扩展的方式与设备通信。
在这篇博文中,我们将概述 Netflix 的快速事件通知系统,并分享我们在此过程中获得的一些经验。
动机
随着会员群的快速增长和系统的日益复杂,Netflix 的架构已经演变成一种异步架构,可以同时支持在线和离线计算。在各种平台(iOS、Android、智能电视、Roku、Amazon FireStick、网络浏览器)和各种设备类型(手机、平板电脑、电视、计算机、机顶盒)上提供无缝且一致的 Netflix 体验,这样需要的不仅仅是传统的request - response 模型。随着时间的推移,我们看到后端系统需要启动与设备的通信以通知其他更新。
用户用例
查看活动 当会员开始观看节目时,他们的“继续观看”列表应该更新到其他所有设备上。
个性化体验刷新 Netflix 推荐引擎不断为每个成员刷新推荐。更新需要及时传送到设备上,以获得最佳的会员体验。
会员计划变更 会员经常改变他们的计划类型,导致他们的体验发生变化,这种变化必须立即反映在他们所有设备上。
会员“我的名单”更新 当成员通过添加或删除标题来更新他们的“我的列表”时,这些更改应该反映在他们的所有设备上。
会员资料变更 当成员更新其帐户设置(例如添加/删除/重命名配置文件)或更改其首选内容成熟度级别时,这些更新必须反映在其所有设备上。
系统诊断信号 在特殊情况下,我们需要向设备上的 Netflix App 发送诊断信号,以帮助解决问题并启用跟踪能力。
设计决策
在设计系统时,我们做出了一些有助于塑造 RENO 架构的关键决策:
单一事件源
事件优先级
混合通信模型
有针对性的交付
管理高 RPS
单一事件源
我们想要支持的用例源自各种内部系统和成员操作,因此我们需要监听来自多个不同微服务的事件。在 Netflix,我们的近实时事件流由一个名为曼哈顿的内部分布式计算框架管理。我们利用曼哈顿的事件管理框架来创建级别,作为 RENO 的单一事件源。
事件优先级
考虑到用例的来源非常广泛,我们在事件处理中做了优先级细分。例如,诸如“配置文件成熟度级别变化”之类的用户触发事件应该比“系统诊断信号”具有更高的优先级。因此,我们为每个用例分配了一个优先级,并通过路由到特定的优先级队列和相应的事件处理集群来分配事件流量。这种分离使我们能够针对不同的事件优先级和流量模式独立调整系统配置和扩展策略。
混合通信模型
正如本文前面提到的,像 RENO 这样的服务面临的一个关键挑战是多平台支持。虽然移动设备几乎总是在线,但智能电视仅在使用时才在线。这种网络连接的异质性使得选择单一的交付模型变得困难。例如,完全依赖拉模式,移动应用切到后台的时候就需要频繁拉取。这反过来将触发 iOS 和 Android 平台的应用程序的通信限制(我们还需要考虑低带宽连接)。另一方面,仅使用推送机制会由于智能电视在一天中的大部分时间处于关机状态而错过通知。因此,我们选择了推拉结合通信模型,其中服务器尝试使用推模式立即将变化送达给所有设备,并且设备在应用程序生命周期的各个阶段也会进行拉取。
使用推拉结合模式还支持仅限于单一通信模型的设备。其中包括不支持推送通知的老旧设备。
有针对性的送达
考虑到我们需要支持很多种源设备和目标设备,我们也构建了对设备特定通知的支持。此功能允许根据用例通知特定的设备类别。当可操作的事件到达时,RENO 应用特定于用例的业务逻辑,收集有资格接收此通知的设备列表并尝试进行通知。这有助于大大限制传出流量。
管理高 RPS
拥有超过 2.2 亿会员,我们意识到像 RENO 这样的服务需要在观看期间处理许多事件。在高峰期,RENO 每秒处理大约 15 万个事件。如此高的 RPS 可能会造成惊群问题,并对内部和外部的下游服务造成压力。
因此,我们做了一些优化:
活动年龄---许多需要通知设备的事件都是时间敏感的,如果不能立即发送,那么用户即使收到该通知,也没啥价值了。为避免处理过期事件,应用过期过滤器作为门控检查。如果事件年龄超过可配置阈值,则不会对其进行处理。此过滤器会在前期处理阶段的清除对设备没有价值的事件,并防止队列因可能已过期的上游事件而被淹没。
在线设备---为了减少流量,通知仅发送到当前在线的设备,实现方法是利用 Zuul 保持最新的注册表。
扩展策略---为了解决惊群问题并将延迟保持在可接受的阈值以下,相比缩容策略我们使用了更积极的集群扩容策略。这种方法使计算能力随着队列增长而增长。
事件重复数据删除---iOS 和 Android 平台都积极限制后台应用程序的活动级别,这就是在 RENO 中对传入事件进行重复数据删除的原因。在高 RPS 的情况下可能会发生重复事件,并且在不会导致设备上下文丢失的情况下将通知合并在一起。
交付隔离---多个下游服务将推送通知发送到不同的设备平台,包括用于 Apple 设备的 Apple Push Notification Service (APNS) 和用于 Android 的 Google Firebase Cloud Messaging (FCM) 等外部平台。为了防止下游服务导致整个通知服务中断,事件传递在不同平台上并行进行。如果下游服务或平台未能送达通知,则不会妨碍其他设备接收推送通知。
系统架构
如上图所示,RENO 服务可以分解为以下组件。
事件触发器
用户以及系统行为,需要在会员的设备上刷新数据。
事件管理引擎
准实时事件流管理框架可以进行配置,从而侦听特定事件并将事件转发到不同的队列。
基于事件优先级的队列
由基于优先级的事件转发规则填充的 Amazon SQS 队列,以允许基于优先级的流量分片。
基于事件优先级的集群
AWS实例集群,以相同的优先级订阅相应的队列。它们处理队列中的事件,并生成通知。
出站消息系统
对于移动设备通过Netflix消息系统来发推送。
对于网络、电视和其他流媒体设备的通知,我们使用一个自制的推送通知解决方案,称为Zuul Push,提供与在线设备的 "永远在线 "的持久连接。
持久存储
使用 Cassandra 数据库,存储 RENO 为每个设备发出的所有通知,允许设备轮询自己的消息。
可观测性
在 Netflix,我们非常重视监控能力,以提供系统健康状况的清晰视图。对于像 RENO 这样的高 RPS 服务,它依赖多个上游系统作为其流量源,同时为不同的内部和外部的下游系统产生大量流量,需要保证强大的可观测性。对于警报,除了 CPU、内存和性能等标准系统健康指标外,我们还添加了许多“服务边缘”指标和日志记录,以捕获上游或下游系统的异常。此外,除了实时警报外,我们还为重要指标添加了趋势分析,以帮助捕捉长期退化。我们使用名为 Mantis 的实时流处理程序来检测 RENO。它使我们能够以设备的粒度实时跟踪事件,从而使调试更容易。最后,我们发现特定平台的报警(适用于 iOS、Android 等)有助于更快地找到问题的根本原因。
优势
可以轻松支持新用例
以更高的吞吐量水平扩展
当我们着手构建 RENO 时,该系统的目标仅限于产品端的“个性化体验刷新”这一用例。随着 RENO 的发展,对新用例的支持成为可能,RENO 很快被定位为 Netflix 所有产品领域的集中化快速通知服务。
我们早期做出的设计决策得到了回报,例如使添加新用例成为“即插即用”解决方案,并提供跨所有平台的混合交付模型。我们能够快速加入其他用例,从而畅通无阻地进行大量创新。
构建这个平台的一个重要经验是确保随着时间的推移需要更多类型的事件和更高的吞吐量,RENO 可以水平扩展。这种能力主要是通过允许基于事件类型或优先级的分片以及使用异步事件驱动的处理模型来实现的,该模型可以通过添加更多机器进行事件处理来扩展系统。
展望未来
随着 Netflix 的会员群快速增长,拥有像 RENO 这样的服务可以帮助我们的会员获得最佳和最新的 Netflix 体验。从与会员相关的更新到上下文个性化等等 - 随着我们不断创新我们的会员体验,我们也不断发展我们的通知组合。在架构上,我们正在评估构建更多功能的机会,例如保证消息传递和消息批处理,这些功能可以扩展更多的场景并帮助减少 RENO 的。
参考阅读:
本文由高可用架构翻译。技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。