全面解析Netflix的微服务架构设计
多年来,Netflix 一直是全球最出色的在线订阅制视频流媒体服务,其流量占全球互联网带宽容量的 15%以上。截至 2019 年,Netflix 已拥有 1.67 亿订阅用户,每个季度新增 500 万订户,服务覆盖全球 200 多个国家 / 地区。Netflix 用户每天在 4000 多部电影和 47000 集电视剧上花费超过 1.65 亿小时的时间。从工程角度看,这些令人印象深刻的统计数据向我们展示了 Netflix 的技术团队设计出了多么优秀的视频流系统;这套系统具有很高的可用性和可扩展性,能为全球客户提供服务。
但是,该公司的技术团队花了超过 8 年时间才打造出今天这样强大的 IT 系统。实际上,Netflix 的基础架构转型始于 2008 年 8 月,当时他们的数据中心遇到了服务中断的故障,导致整个 DVD 租赁服务关闭三天。Netflix 意识到,它需要一个没有单点故障的更可靠的基础架构。因此它做出两个重要决定:将 IT 基础架构从自己的数据中心迁移到公共云上,并通过微服务架构,用较小的易管理软件组件替换单体程序。这两个决定为今天 Netflix 的成功打下了坚实基础。
Netflix 之所以选择 AWS 云来迁移其 IT 基础架构,是因为 AWS 可以在全球范围内提供高度可靠的数据库、大规模云存储和众多数据中心。Netflix 利用了由 AWS 构建和维护的云基础架构,从而免去自建数据中心的繁重重复劳动,并将更多精力放在提供高质量视频流体验的核心业务上。尽管 Netflix 必须重建整个技术体系,以使其能在 AWS 云上平稳运行,但作为回报,系统的可扩展性和服务可用性得到显著提高。
Netflix 还是微服务架构背后的首批主要推动者之一。微服务鼓励关注点分离来解决单体软件设计存在的问题。在这种架构中,大型程序通过模块化和独立的数据封装被分解为许多较小的软件组件。微服务还通过水平扩展和工作负载分区来提升可扩展性。采用微服务后,Netflix 工程师可以轻松更改任何服务,从而加快部署速度。更重要的是,他们能跟踪每个服务的性能水平,并在其出现问题时与其他正在运行的服务快速隔离开来。
Netflix 基于亚马逊云计算服务(AWS),及公司内部的内容交付网络 Open Connect 运营。两套系统必须无缝协作才能在全球范围内提供高质量的视频流服务。从软件架构的角度来看,Netflix 包括三大部分:客户端、后端和内容交付网络(CDN)。
客户端是用户笔记本电脑或台式机上所有受支持的浏览器,或者智能手机 / 智能电视上的 Netflix 应用。Netflix 开发了自己的 iOS 和 Android 应用,试图为每个客户端和每台设备都能提供最佳的观看体验。Netflix 可以通过其 SDK 控制自己的应用和其他设备,从而在某些场景下(例如网络速度缓慢或服务器超载)透明地调整流服务。
后端包括完全在 AWS 云上运行的服务、数据库和存储。后端基本上可以处理不涉及流视频的所有内容。后端的某些组件及其对应的 AWS 服务列举如下:
可扩展计算实例(AWS EC2)
可扩展存储(AWS S3)
业务逻辑微服务(Netflix 专用框架)
可扩展的分布式数据库(AWS DynamoDB、Cassandra)
大数据处理和分析作业(AWS EMR、Hadoop、Spark、Flink、Kafka 和一些 Netflix 专用工具)
视频处理和转码(Netflix 专用工具)
Open Connect CDN 是称为 Open Connect Appliances(OCAs)的服务器网络,已针对存储和流传输大尺寸视频进行了优化。这些 OCA 服务器放置在世界各地的互联网服务提供商(ISP)和互联网交换位置(IXP)网络内。OCA 负责将视频直接流传输到客户端。
当订阅者单击应用或设备上的播放按钮时,客户端将与 AWS 上的后端和 Netflix CDN 上的 OCA 对话以流传输视频。下图说明了 playback 流程的工作机制:
用于流视频的 playback 架构
OCA 不断将关于其负载状态、可路由性和可用视频的运行状况报告发送到在 AWS EC2 中运行的缓存控制(Cache Control)服务上,以便 Playback 应用向客户端更新最新的健康 OCA。
播放(Play)请求从客户端设备发送到在 AWS EC2 上运行的 Netflix 播放应用服务,以获取流视频的 URL。
Playback 应用服务必须确定播放请求是有效的,才能观看特定视频。这里的验证流程将检查用户的订阅计划,以及在不同国家 / 地区的视频许可等。
客户端从 Playback 应用服务返回的 10 个 OCA 服务器的列表中测试这些 OCA 的网络连接质量,并选择最快、最可靠的 OCA 来请求视频文件,进行流传输。
选定的 OCA 服务器接受来自客户端的请求并开始流传输视频。
在上图中,Playback 应用服务、引导服务和缓存控制服务完全在基于微服务架构的 AWS 云中运行。在下一节中我将介绍 Netflix 后端微服务架构,该架构可提高当前服务的可用性和可扩展性。
如前所述,后端要处理几乎所有内容,从注册、登录、计费到更复杂的处理任务,如视频转码和个性化推荐等无所不包。为同时支持在同一底层基础架构上运行的轻量与重量级负载,Netflix 为其基于云的系统选择了微服务架构。图 2 展示了 Netflix 可能使用的微服务架构,我从一些在线资源中总结出了这些架构形态:
基于多种来源分析得出的后端架构参考
客户端向在 AWS 上运行的后端发送一个播放请求。该请求由 AWS 负载均衡器(ELB)处理。
AWS ELB 会将请求转发到在 AWS EC2 实例上运行的 API 网关服务上。名为 Zuul 的组件是由 Netflix 团队构建的,提供动态路由、流量监控和安全性,以及对云部署边缘故障的恢复能力。该请求将应用在与业务逻辑对应的一些预定义过滤器上,然后转发到应用程序(Application)API 做进一步处理。
应用程序 API 组件是 Netflix 运营背后的核心业务逻辑。有几种类型的 API 对应不同的用户活动,如注册 API 和用于检索视频推荐的推荐 API 等。在这里,来自 API 网关服务的转发请求由播放 API 处理。
播放 API 将调用一个或一系列微服务来满足请求。图 1 中的播放应用服务、引导服务和缓存控制服务可以视为微服务。
微服务主要是无状态的小型程序,并且也可以相互调用。为了控制其级联故障并获得弹性,Hystrix 将每个微服务与调用者进程隔离开来。其运行结果可以缓存在基于内存的缓存中,以更快地访问那些关键的低延迟请求。
微服务能在流程中保存到数据存储或从数据存储中获取数据。
微服务可以将用于跟踪用户活动或其他数据的事件发送到流处理管道(Stream Processing Pipeline),以实时处理个性化推荐任务,或批处理业务智能任务。
来自流处理管道的数据能持久存储到其他数据存储中,如 AWS S3、Hadoop HDFS 和 Cassandra 等。
上述架构可以帮助我们概括了解系统的各个部分如何组织和协同工作以流传输视频。但要分析这一架构的可用性和可扩展性,我们需要深入研究每个重要组件,以了解其在不同负载下的性能表现。下一节将具体介绍这部分内容。
本节会深入研究第 2 节中定义的组件,以分析其可用性和可扩展性。在介绍每个组件时,我还将说明它们是如何满足这些设计目标的。在后面的章节中将对整个系统进行更深入的设计分析。
Netflix 技术团队投入了大量精力来开发能在笔记本、台式机或移动设备上运行得更快、更智能的客户端应用。即使在某些没有专用 Netflix 客户端的智能电视上,Netflix 仍然可以通过自己提供的 SDK 来控制设备的性能表现。实际上,任何设备环境都需要安装 Netflix Ready Device Platform(NRDP),以实现最佳的观看体验。图 3 展示了一个典型的客户端结构组件。
客户端应用组件
客户端应用(Client App)会将自己与后端的连接分为 2 种类型,分别用于内容发现(Content discovery)和内容播放。客户端对播放请求使用 NTBA 协议,以确保其 OCA 服务器位置具有更高的安全性,并消除了新连接的 SSL/TLS 握手引起的延迟。
在流传输视频时,如果网络连接过载或出现错误,客户端应用会智能地降低视频质量或切换到其他 OCA 服务器上。即使连接的 OCA 过载或出现故障,客户端应用也可以轻松切换为其他 OCA 服务器,以获得更好的观看体验。之所以客户端能实现所有这些目标,是因为其上的 Netflix Platform SDK 一直在跟踪从播放应用服务中检索到的最新健康 OCA 列表。
API 网关服务(API Gateway Service)组件与 AWS 负载均衡器(Load Balancer)通信以解析来自客户端的所有请求。该组件可以部署到位于不同区域的多个 AWS EC2 实例上,以提高 Netflix 服务的可用性。图 4 展示了开源的 Zuul,这是 Netflix 团队创建的 API 网关的实现。
Zuul 网关服务组件
入站过滤器(Inbound Filter)可用于身份验证、路由和装饰请求。
端点过滤器(Endpoint Filter)可用于返回静态资源或将请求路由到适当的 Origin 或应用程序 API 做进一步处理。
出站过滤器(Outbound Filter)可用于跟踪指标、装饰对用户的响应或添加自定义标头。
Zuul 集成了服务发现组件 Eureka,从而能够发现新的应用程序 API。
https://github.com/netflix/eureka
Zuul 被广泛用于各种用途的流量路由任务上,例如启用新的应用程序 API、负载测试、在负载很大的情况下路由到不同的服务端点上,等等。
应用程序 API 充当 Netflix 微服务的业务流程层(也称编排层)。这种 API 提供了一种逻辑,按所需顺序组装对底层微服务的调用,并带有来自其他数据存储的额外数据以构造适当的响应。Netflix 团队花了很多时间设计应用程序 API 组件,因为它对应 Netflix 的核心业务功能。它还需要在高请求量下具有可扩展和高可用性。当前,应用程序 API 分为三类:用于非会员请求(例如注册、下单和免费试用等)的注册(Signup)API,用于搜索和发现请求的发现(Discovery)API,以及用于流视频和查看许可请求的播放API。图 5 提供了应用程序 API 的详细结构组件图。
播放和发现应用程序 API 的分离
在播放 API 实现的最新更新中,播放 API 和微服务之间的网络协议是 gRPC/HTTP2,它“允许通过协议缓冲区定义 RPC 方法和实体,并自动以多种语言生成客户端库 /SDK”。此项更改使应用程序 API 可以通过双向通信与自动生成的客户端进行适当集成,并“尽可能减少跨服务边界的代码重用”。
应用程序 API 还提供了一种基于 Hystrix 命令的通用弹性机制,以保护其底层微服务。
由于应用程序 API 必须处理大量请求并构造适当的响应,因此其内部处理工作需要高度并行运行。Netflix 团队发现正确的方法是同步执行和异步 I/O 相结合应用。
应用程序 API 的同步执行和异步 I/O
来自 API 网关服务的每个请求都将放入应用程序 API 的网络事件循环(Network Event Loop)中处理;
每个请求将被一个专用的线程处理程序阻止,该处理程序将 Hystrix 命令(如 getCustomerInfo 和 getDeviceInfo 等)放入传出事件循环(Outgoing Event Loop)中。传出事件循环是针对每个客户端设置的,并以非阻塞 I/O 运行。一旦调用的微服务完成或超时,上述专用线程将构造对应的响应。
按照 Martin Fowler 的 定义,“微服务是一组小型服务,每个小服务都在自己的进程中运行,并使用轻量机制通信……”。这些小型程序可以独立部署或升级,并具有自己的封装数据。
Netflix 上的微服务组件实现如图 7 所示。
微服务的结构化组件
微服务可以独立工作,也能通过 REST 或 gRPC 调用其他微服务。
微服务的实现可以类似于图 6 中描述的应用程序 API 的实现:请求将被放入网络事件循环中,而来自其他被调用的微服务的结果将放入异步非阻塞 I/O 中的结果队列。
每个微服务能拥有自己的数据存储和一些存放近期结果的内存缓存存储。EVCache 是Netflix 微服务缓存的主要选择。
https://github.com/Netflix/EVCache
Netflix 将其基础架构迁移到 AWS 云时,针对不同的用途使用了不同的数据存储(图 8),包括 SQL 和 NoSQL。
部署在 AWS 上的 Netflix 数据存储
MySQL 数据库用于电影标题管理和交易 / 下单目的。
Hadoop 用于基于用户日志的大数据处理。
ElasticSearch 为 Netflix 应用提供了标题搜索能力。
Cassandra 是基于列的分布式 NoSQL 数据存储,可处理大量读取请求,而没有单点故障。为了优化大规模写请求的延迟,Netflix 使用了 Cassandra,因为它具有最终的一致性能力。
流处理数据管道(Stream Processing Data Pipeline)已成为 Netflix 业务分析和个性化推荐任务的数据骨干。它负责实时生成、收集、处理和汇总所有微服务事件,并将其移动到其他数据处理器上。图 9 展示了该平台的各个部分。
Netflix 的 Keystone 流处理平台
这一流处理平台每天处理数以万亿计的事件和 PB 级的数据。随着订户数量的增加,它也会自动扩展。
路由(Router)模块支持路由到不同的数据 sink 或应用程序上,而 Kafka 负责路由消息,并为下游系统提供缓冲。
流处理即服务(SPaaS)使数据工程师可以构建和监视他们自定义的可管理流处理应用程序,而平台将负责可扩展性和运维。
Open Connect 是一个全球内容交付网络(CDN),负责存储 Netflix 电视节目和电影并将其交付给全世界的订户。Netflix 为了让人们想要观看的内容尽可能靠近他们想要观看的位置,而构建和运营了 Open Connect 这一高效率的网络。为了将观看 Netflix 视频的流量导向到客户的当地网络中,Netflix 已与世界各地的互联网服务提供商(ISP)和互联网交换点(IX 或 IXP)合作,以在这些合作伙伴的网络内部部署称为 Open Connect Appliances(OCA)的专用设备。
将 OCA 部署到 IX 或 ISP 站点
OCA 是经过优化的服务器,用于存储来自 IX 或 ISP 站点的大型视频文件,并直接流式传输到订户的家中。这些服务器会定期向 AWS 上的 Open Connect 控制平面(Control Plane)服务报告自己的运行状况指标,包括它们从 IXP/ISP 网络学到的最佳路径,以及自己的 SSD 上都存储了哪些视频等信息。反过来,控制平面服务将根据这些数据中反映的文件可用性、服务器健康状况以及与客户端的网络距离等指标,自动引导客户端设备到最佳的 OCA 上。
控制平面服务还控制每晚在 OCA 上添加新文件或更新文件的填充(filling)行为。填充行为如图 11 所示。
当新的视频文件已成功转码并存储在 AWS S3 上时,AWS 上的控制平面服务会将这些文件传输到 IXP 站点上的 OCA 服务器上。这些 OCA 服务器将应用缓存填充(cache fill),将这些文件传输到其子网下 ISP 站点上的 OCA 服务器上。
当 OCA 服务器成功存储视频文件后,它将能够启用对等填充(peer fill),以根据需要将这些文件复制到同一站点内的其他 OCA 服务器上。
OCA 之间的填充模式
在前面的章节中,我详细介绍了为 Netflix 视频流业务提供支持的云架构及其组件。在本节和后续章节中,我想更深入地分析这种设计架构。我会从最重要的设计目标列表开始,如下所示:
确保全球范围内流服务的高可用性;
弹性处理网络故障和系统中断;
在各种网络条件下,将每台受支持设备的流传输延迟降至最低;
支持高请求量的可扩展性
在下面的小节中,我将分析流服务的可用性及其对应的最佳延迟。第 6 节是关于弹性机制(例如混沌工程)的更深入分析,而第 7 节介绍了流服务的可扩展性。
根据定义,系统的可用性是用一段时间内对请求的响应有多少次来衡量的,但不能保证响应包含了信息的最新版本。在我们的系统设计中,流服务的可用性是由后端服务和保存流视频文件的 OCA 服务器的可用性共同决定的。
后端服务的目标是通过缓存或某些微服务的执行来获取最接近特定客户端的健康 OCA 列表。因此,其可用性取决于涉及播放请求的众多组件:负载均衡器(AWS ELB)_ 代理服务器(API 网关服务)、播放 API、微服务的执行、缓存存储(EVCache)和数据存储(Cassandra):
负载均衡器可以将流量路由到不同的代理服务器上以帮助防止负载超载,从而提高可用性;
播放 API 通过 Hystrix 命令控制超时微服务的执行,从而防止级联故障影响其他服务;
如果微服务对外部服务或数据存储的调用所花费的时间超出预期,则它可以使用缓存中的数据响应播放 AI;
缓存会被复制以加快访问速度。
当客户端从后端接收到 OCA 服务器列表时会在网络上探测这些 OCA,并选择最佳的 OCA 进行连接。如果该 OCA 在流处理过程中超载或失败,则客户端将切换到另一个状态良好的 OCA 上,否则 Platform SDK 将请求其他 OCA。因此,其可用性与 ISP 或 IXP 中所有可用 OCA 的可用性高度相关。
Netflix 流服务的高可用性是以复杂的多区域 AWS 运维和服务,以及 OCA 服务器的冗余为代价的。
流服务的等待时间主要取决于播放 API 能多快地解析健康的 OCA 列表,以及客户端与所选 OCA 服务器的连接健康水平。
正如我在应用程序 API 组件部分中所述,播放 API 不会永远等待微服务的执行,因为它使用 Hystrix 命令来控制获取到结果之前要等待的时间,一旦超时就会从缓存获取非最新数据。这样做可以将延迟控制在可接受的水平上,还能避免级联故障影响更多服务。
如果当前选定的 OCA 服务器出现网络故障或超载,则客户端将立即切换到其他具有最可靠网络连接的 OCA 服务器上。如果发现网络连接质量下降,它也可以降低视频质量以使其与网络质量相匹配。
经过认真考虑,在上述系统设计中已经做出了两个重要的权衡:
用一致性换取低延迟;
用一致性换取高可用性
该系统后端服务的架构设计选择了用一致性来换取低延迟。播放 API 可以从 EVCache 存储或最终一致的数据存储(如 Cassandra)中获取过时的数据。
类似地,所谓用一致性换取高可用性的权衡是说,系统希望以可接受的延迟发起响应,而不会对像 Cassandra 这样的数据存储中的最新数据执行微服务。
在可扩展性和性能之间还存在不完全相关的权衡。在这种权衡下,通过增加实例数量来处理更多负载来提高可扩展性,可能会导致系统达不到预期的性能提升水平。对于那些无法在可用 worker 之间很好地平衡负载的设计架构来说,这可能是个问题。但是,Netflix 通过 AWS 自动扩展解决了这一矛盾。我们将在第 7 节中具体讨论这个解决方案。
从迁移到 AWS 云的那一天起,设计一套能够从故障或停机中自我恢复的云系统就一直是 Netflix 的长期目标。该系统已解决的一些常见故障如下:
解析服务依赖项时失败;
执行微服务时的失败,导致级联失败影响其他服务;
由于过载导致无法连接到某个 API 上;
连接到实例或服务器(如 OCA)时失败;
为了检测并解决这些故障,API 网关服务 Zuul 提供了一些内置功能,如自适应重试和限制对应用程序 API 的并发调用等。反过来说,应用程序 API 使用 Hystrix 命令来使对微服务的调用超时,以停止级联故障并将故障点与其他服务隔离开来。
Netflix 技术团队也以其在混沌工程上的实践而闻名。这个想法是将伪随机错误注入生产环境,并构建解决方案以自动检测、隔离这类故障,并从中恢复。这些错误可能会增加执行微服务的响应的延迟、杀死服务、停止服务器或实例,甚至可能导致整个区域的基础架构瘫痪。通过有目的地使用检测和解决此类故障的工具,将现实的生产故障引入受监控的环境,Netflix 可以在这类缺陷造成较大问题之前提早发现它们。
在本节中,我将介绍水平扩展、并行执行和数据库分区这些 Netflix 的流服务可扩展性要素。缓存和负载均衡等要素也有助于提高可扩展性,它们已在第 4 节中提到了。
首先,AWS 自动扩展(Auto Scaling)服务提供了 Netflix 上 EC2 实例的水平扩展能力。当请求量增加时,这个 AWS 服务将自动启动更多弹性实例,并关闭未使用的实例。更具体地说,在成千上万个此类实例的基础上,Netflix 构建了一个开源容器管理平台 Titus,其每周可运行约 300 万个容器。同样,图 2 架构中的任何组件都可以部署在容器内。此外,Titus 允许容器运行在全球各大洲的多个区域内。
其次,第 3.2.2 节中应用程序 API 或微服务的实现还允许在网络事件循环和异步传出事件循环上并行执行任务,从而提高了可扩展性。
最后,宽列存储(如 Cassandra)和键值对象存储(如 ElasticSearch)还提供了高可用性和高可扩展性,同时没有单点故障。
总的来说,Netflix 的云架构已经过了其生产系统的验证,可以为在数千个虚拟服务器上运行的数百万个订户提供服务;该架构还通过与 AWS 云服务的集成在全球范围内提供了高可用性、最佳延迟、强大的可扩展性以及对网络故障和系统故障的恢复能力。本文提到的大多数架构和组件都是通过互联网上的可信在线资源学习总结出来的。尽管网上没有太多资源能直接介绍这些微服务的内部实现,以及监视其性能表现的工具和系统,但本文的研究成果可以作为构建典型生产系统的参考实现。
延展阅读:
https://medium.com/swlh/a-design-analysis-of-cloud-based-microservices-architecture-at-netflix-98836b2da45f
点个在看少个 bug 👇