Netflix 中 Java 使用的演变

当小于 80%的用户流量经过测试 发货前。 但如何获得这种覆盖范围呢? 您要么花费数年时间扩展内部质量检查,要么您 与 QA Wolf 一起在短短 4 个月内实现目标

怎么样 质量保证沃尔夫 不同的?

  • 他们不按小时收费。

  • 他们保证结果。

  • 他们提供运行 15 分钟 QA 周期所需的所有工具和(并行运行)基础设施。

在这里获取快速演示

Netflix 主要是一家 Java 商店。

Netflix 的每个后端应用程序都是 Java 应用程序。 这包括:

然而,这并不意味着 Netflix 的 Java 堆栈是静态的。 多年来,它已经发生了显着的发展。

在这篇文章中,我们将根据为支持不断变化的需求而发生的整体架构变化来了解 Netflix Java 使用的演变。

众所周知,Netflix 采用微服务架构。

每一项功能和数据都由一个微服务拥有,并且有数千个微服务。 此外,多个微服务相互通信以实现一些更复杂的功能。

例如,当您打开 Netflix 应用程序时,您会看到 LOLOMO 屏幕。 在这里,LOLOMO 代表电影列表,它本质上是通过从许多微服务获取数据来构建的,例如:

  • 返回前 10 部电影列表的服务

  • 为每部电影提供个性化图像的艺术品服务

  • 返回电影标题、演员详细信息和描述的电影元数据服务

  • LOLOMO 服务,提供为用户主页实际呈现的列表。

下图显示了这种情况。

在 Netflix 应用程序上渲染一个屏幕很可能需要调用 10 个服务。

但是,从设备(例如电视)或移动应用程序调用如此多的服务通常效率很低。 进行 10 次网络调用无法扩展并会导致糟糕的客户体验。 许多流媒体应用程序都遇到此类性能问题。

为了避免这些问题,Netflix 对各种 API 使用了一个前门。 该设备调用此前门,对所有不同的微服务执行扇出。 前门充当网关,Netflix 使用 Zuul 来实现此目的。

这种方法之所以有效,是因为对多个微服务的调用发生在内部网络上,速度非常快,从而消除了性能影响。

然而,还有另一个问题需要解决。

用户可以用来访问 Netflix 的所有不同设备都有细微的不同要求。 虽然 Netflix 试图在每个设备上保持 UI 及其行为的一致外观和感觉,但每个设备在内存或网络带宽方面仍然有不同的限制,因此加载数据的方式略有不同。

很难创建一个可以在所有这些不同设备上运行的 REST API。 部分问题如下:

  • REST API 获取的数据过多或过少

  • 即使他们创建一个 REST API 来满足所有数据需求,这也将是一种糟糕的体验,因为他们会浪费大量数据

  • 如果有多个 API,则意味着多个网络调用

为了解决这个问题,Netflix 使用了后端换前端 (BFF) 模式。

在这种模式中,每个前端或 UI 都有自己的迷你后端。 迷你后端负责执行扇出并获取 UI 在该特定点所需的数据。

下图描述了 BFF 模式的概念。

就 Netflix 而言,BFF 本质上是针对特定设备上特定屏幕的 Groovy 脚本。

这些脚本是由 UI 开发人员编写的,因为他们知道渲染特定屏幕所需的确切数据。 编写完成后,脚本将部署在 API 服务器上,并通过调用适当的 Java 客户端库对所有不同的微服务执行扇出。 这些客户端库是 gRPC 服务或 REST 客户端的包装器。

下图显示了此设置。

如果您不是付费订阅者,以下是您本月错过的内容。

  1. IPv4 寻址速成班

  2. Netflix 扩展简史

  3. 15 个改变世界的开源项目

  4. 导致你丢掉工作的三大简历错误

  5. 视频推荐的工作原理 – 第 1 部分

要接收所有完整文章并支持 ByteByteGo,请考虑订阅:

Groovy 脚本帮助执行扇出。

但在 Java 中进行这样的扇出并非易事。 传统的方法是创建一堆线程并尝试使用最少的线程管理来管理扇出。

然而,由于容错,事情很快就变得复杂了。 在处理多个服务时,您可能会发现其中一个服务响应不够快或失败,从而导致您必须清理线程并确保一切正常工作。

这就是 RxJava 和反应式编程帮助 Netflix 通过处理所有线程管理复杂性以更好的方式处理扇出的地方。

在 RxJava 之上,Netflix 创建了一个名为 Hystrix 的容错库,负责故障转移和隔离。 尽管反应式编程很复杂,但它在当时很有意义,而且该架构使它们能够满足 Netflix 的大部分流量需求。

然而,这种方法有一些重要的局限性:

  • 每个端点都有一个脚本,导致需要维护和管理大量脚本

  • UI 开发人员必须创建所有迷你后端,并且他们不喜欢使用 RxJava 在 Groovy Java 空间中工作。 让事情变得困难的并不是他们日常使用的主要语言

  • 响应式编程通常很困难并且学习曲线陡峭。

在过去几年中,Netflix 的 Java 服务一直在迁移到全新的架构。 这个新架构的核心是 GraphQL Federation。

当您将 GraphQL 与 REST 进行比较时,主要区别在于 GraphQL 始终具有模式。 该架构有助于定义一些关键方面,例如:

例如,对于 Netflix,您可能需要查询返回节目类型的所有节目。 它有一个节目作为标题,还包含评论,这可能是另一种类型。

使用 GraphQL,客户端必须明确字段选择。 您不能只要求观看演出并从演出中获取所有数据。 相反,你必须特别提到你想要获得节目的标题和各种评论的分数。 如果你不要求田地,你就不会得到田地。

对于 REST,情况正好相反,因为您可以获得 REST 服务决定发送的任何内容。

虽然客户端在 GraphQL 中指定查询需要做更多工作,但它解决了过度获取的整个问题,即您获得的数据比实际需要的数据多得多。 这为创建一个可以服务所有不同 UI 的 API 铺平了道路。

为了增强 GraphQL,Netflix 更进一步,使用 GraphQL Federation 将其重新融入到他们的微服务架构中。

下图显示了 GraphQL Federation 的设置。

如您所见,微服务现在称为 DGS 或域图服务。

DGS 是 Netflix 开发的用于构建 GraphQL 服务的内部框架。 当他们开始转向 GraphQL 和 GraphQL Federation 时,还没有任何成熟到足以在 Netflix 规模使用的 Java 框架。 因此,他们构建在低级 GraphQL Java 框架之上,并通过模式类型的代码生成和联合支持等功能对其进行了增强。

从本质上讲,DGS 只是一个具有 GraphQL 端点和模式的 Java 微服务。

虽然有多个 DGS,但从电视等设备的角度来看,只有一个大的 GraphQL 模式。 该模式包含所有可能呈现的数据。 设备不需要担心后端架构中所有不同的微服务。

例如,LOLOMO DGS 可以仅使用标题来定义类型节目。 然后,图像 DGS 可以扩展该类型显示并向其添加艺术品 URL。 两个不同的 DGS 彼此不了解任何信息。 他们所需要做的就是将其架构发布到联合网关。 联合网关知道如何与 DGS 通信,因为它们都有 GraphQL 端点。

这种设置有几个优点:

  • 不再有 API 重复。

  • 不需要后端换前端(BFF),因为 GraphQL 作为 API 足够灵活,由于字段选择功能可以支持不同的设备。

  • 无需 UI 工程师进行任何服务器端开发。 后端开发人员和 UI 开发人员只是在架构上进行协作。

  • 不再需要任何 Java 客户端库。 这是因为联合网关知道如何与通用 GraphQL 服务通信,而无需编写特定代码。

最近,Netflix 已从 Java 8 迁移到 Java 17。迁移后,他们发现 Java 17 上的 CPU 使用率比 Java 8 提高了约 20%,而无需任何代码更改。 这是因为 G1 垃圾收集器的改进。 就 Netflix 的规模而言,CPU 利用率提高 20% 对于成本效益而言意义重大。

与普遍看法相反,Netflix 没有自己的 JVM。 他们只是使用 Azul Zulu JVM,这是一个 OpenJDK 版本。

总体而言,Netflix 拥有大约 2800 个 Java 应用程序,其中大部分是不同规模的微服务。 此外,他们还有大约 1500 个内部图书馆。 其中一些是实际的库,而许多只是位于 gRPC 或 REST 服务前面的客户端库。

对于构建系统,Netflix 依赖 Gradle。 在 Gradle 之上,他们使用 Nebula,这是一组开源 Gradle 插件。 Nebula 最重要的方面是库的解析。 Nebula 有助于版本锁定,从而有助于可重复的构建。

最近,Netflix 一直在积极测试和推出 Java 21 的更改。与从 Java 8 到 Java 17 的迁移相比,从 Java 17 到 21 的迁移要容易得多。Java 21 还提供了一些重要的功能,例如:

  • 虚拟线程允许以每个请求线程的方式编写的服务器端应用程序以最佳硬件利用率进行扩展。 在每个请求一个线程的风格中,当一个请求到来时,服务器会为其提供一个线程。 请求的所有工作都发生在该线程中

  • 更新的 ZGC 垃圾收集器,专注于低暂停时间,并且在更广泛的用例中运行良好。

  • 结合记录和模式匹配的面向数据的编程

Netflix 因使用 Spring Boot 而闻名。

在过去一年左右的时间里,他们已经完全脱离了基于 Guice 的本土 Java 堆栈,并在 Spring Boot 上完全标准化。

为什么选择 Spring Boot?

它是最流行的 Java 框架,多年来一直得到很好的维护。

Netflix 发现利用 Spring 框架的庞大开源社区、现有文档以及易于获得的培训机会有很多好处。 Spring 的演变及其功能与 Netflix 的核心原则“高度对齐、松散耦合”。

Netflix 使用最新版本的 OSS Spring Boot,他们的目标是尽可能接近开源社区。 然而,为了与 Netflix 生态系统和基础设施紧密集成,他们还创建了 Spring Boot Netflix,它是一堆构建在 Spring Boot 之上的模块。

Spring Boot Netflix 支持多种功能,例如:

  • gRPC 客户端

  • 与 Netflix SSO 堆栈集成的服务器支持,用于 AuthZ 和 AuthN

  • 跟踪、指标和分布式日志记录形式的可观察性

  • 支持 mTLS 的 HTTP 客户端

  • 使用 Eureka 进行服务发现

  • AWS/Titus 集成

  • Kafka、Cassandra 和 Zookeeper 集成

Netflix 并不存在单一的堆栈。

Netflix Java 堆栈在过去几年中一直在发展,从内部框架开始到 Groovy 时代的微服务,最近又转向 GraphQL Federation。

所有更改都是为了解决以前方法中的问题。 例如,迁移到 RxJava 是为了以更好的方式处理扇出,迁移到 GraphQL Federation 是为了解决 RxJava 带来的复杂性问题。

除了这些变化之外,Java 语言版本也发生了并行演变,从 Java 8 到 17,再到现在的 21+。 Spring Boot 版本 3 最终超越了 Java 8 并迫使整个生态系统进行升级,这也促使了很多变化。

这些变化使他们能够构建性能更高的应用程序,从而节省 CPU 成本并

总体而言,主题是在整个组织中构建微服务的方法的标准化。 然而,考虑到在保持竞争优势的同时大规模运营所面临的持续挑战,这种演变仍将继续。

参考:

让您的产品展示在超过 500,000 名技术专业人士面前。

我们的时事通讯将您的产品和服务直接呈现在重要受众面前 – 数十万工程领导者和高级工程师 – 他们对重大技术决策和大宗采购具有影响力。

空间很快就满了 – 立即预订

广告位通常会提前约 4 周售完。 为了确保您的广告吸引到这些有影响力的受众,请立即通过电子邮件预订您的空间 [email protected]

Leave a Reply

Your email address will not be published. Required fields are marked *

近期新闻​

编辑精选​