应用程序扩展简介

可扩展性可以描述为系统增长和适应不断变化的条件的能力。 可扩展性不仅仅局限于纯粹的技术增长; 它还取决于企业及其背后的组织的增长。

如果您正在构建下一个“独角兽初创公司”,并且希望您的产品能够迅速覆盖全球数百万用户,那么您将面临严峻的可扩展性挑战。 您的应用程序将如何满足不断增长的需求? 随着时间的推移,系统是否会变慢或经常崩溃? 如何存储大量数据并控制 I/O? 当您雇佣更多的人时,如何有效地组织不同的团队并使他们能够自主工作,而不会在代码库的不同部分之间发生争用?

即使您没有从事大规模项目,也不意味着您不会担心可扩展性。 您将面临不同类型的可扩展性挑战。 如果对这些挑战没有做好准备,可能会严重阻碍项目的成功,并最终损害项目背后的公司。 在特定项目的背景下实现可扩展性并了解当前和未来业务需求的期望非常重要。

由于可扩展性是一个如此广泛的话题,因此在本章中,我们将重点讨论 Node.js 在可扩展性背景下的作用。 我们将讨论用于扩展 Node.js 应用程序的几种有用的模式和架构。

有了这些工具并充分了解您的业务环境,您将能够设计和实现能够适应和满足您的业务需求并让您的客户满意的 Node.js 应用程序。

扩展 Node.js 应用程序

我们已经知道,典型 Node.js 应用程序的大部分工作负载都在单个线程的上下文中运行。 在第 1 章 Node.js 平台中,我们了解到这不一定是限制,而是优势,因为它允许应用程序优化处理并发请求所需的资源的使用,这要归功于非阻塞 I/ 噢范式。 此模型非常适合每秒处理中等数量请求(通常每秒数百个)的应用程序,特别是如果应用程序主要执行 I/O 密集型任务(例如,从文件系统和网络读取和写入) 而不是受 CPU 限制的任务(例如,数字运算和数据处理)。

无论如何,假设我们使用商用硬件,单个线程可以支持的容量是有限的。 这与服务器的功能有多么强大无关,因此如果我们想将 Node.js 用于高负载应用程序,唯一的方法就是跨多个进程和机器进行扩展。

然而,工作负载并不是扩展 Node.js 应用程序的唯一原因。 事实上,使用允许我们扩展工作负载的相同技术,我们可以获得其他所需的属性,例如高可用性和容错性。 可扩展性也是一个适用于应用程序的大小和复杂性的概念。 事实上,构建可以随着时间的推移根据需要增长的架构是设计软件时的另一个重要因素。

JavaScript 是一个需要谨慎使用的工具。 缺乏类型检查及其许多陷阱可能会成为应用程序发展的障碍,但通过纪律和准确的设计,我们可以将其一些缺点转化为宝贵的优势。 对于 JavaScript,我们经常被迫保持应用程序简单并将其组件分割成小的、可管理的部分。 这种思维方式可以使构建分布式和可扩展的应用程序变得更加容易,而且也易于随着时间的推移而发展。

可扩展性的三个维度

在谈论可扩展性时,要理解的第一个基本原则是负载分配,这是将应用程序的负载分配到多个进程和机器之间的科学。 实现这一目标的方法有很多,Martin L. Abbott 和 Michael T. Fisher 所著的《可扩展性的艺术》一书提出了一种巧妙的模型来表示它们,称为比例立方体。 该模型从以下三个维度描述了可扩展性:

  • X 轴 — 克隆

  • Y 轴 — 按服务/功能分解

  • Z 轴 — 按数据分区划分

这三个维度可以表示为一个立方体,如图12.1所示:

image 2024 05 08 11 09 01 713
Figure 1. 图 12.1:比例立方体

立方体的左下角(即 X 轴和 Y 轴之间的交点)表示应用程序在单个代码库中具有所有功能并在单个实例上运行。 这就是我们通常所说的单体应用程序。 对于处理小型工作负载或处于开发早期阶段的应用程序来说,这是一种常见情况。 给定一个整体应用程序,可以使用三种不同的策略来扩展它。 通过查看比例立方体,这些策略表示为沿立方体不同轴的增长:X、Y 和 Z:

  • X 轴 — 克隆:整体式、未扩展的应用程序最直观的演变是沿着 X 轴向右移动,这很简单,大多数时候成本低廉(就开发成本而言)并且非常有效。 该技术背后的原理很简单,即克隆同一应用程序 n 次,并让每个实例处理 1/n 的工作负载。

  • Y 轴 — 按服务/功能分解:沿 Y 轴扩展意味着根据应用程序的功能、服务或用例分解应用程序。 在这种情况下,分解意味着创建不同的、独立的应用程序,每个应用程序都有自己的代码库,可能有自己的专用数据库,甚至有单独的 UI。

    例如,一种常见的情况是将应用程序中负责管理的部分与面向公众的产品分开。 另一个例子是提取负责用户身份验证的服务,从而创建专用的身份验证服务器。

    按功能拆分应用程序的标准主要取决于其业务需求、用例、数据和许多其他因素,正如我们将看到的 在本章后面。 有趣的是,这是影响最大的扩展维度,不仅影响应用程序的架构,还影响从开发和运营角度管理应用程序的方式。 正如我们将看到的,微服务是一个最常与细粒度 Y 轴缩放相关的术语。

  • Z 轴 — 按数据分区拆分:最后一个缩放维度是 Z 轴,其中应用程序以每个实例仅负责整个数据的一部分的方式进行拆分。 这是数据库中经常使用的技术,也称为水平/垂直分区。 在此设置中,同一应用程序有多个实例,每个实例都在使用不同标准确定的数据分区上运行。

    例如,我们可以根据国家/地区(列表分区)或姓氏首字母(范围分区)对应用程序的用户进行分区,或者让哈希函数决定每个用户所属的分区(哈希分区) )。

    然后可以将每个分区分配给应用程序的特定实例。 使用数据分区要求每个操作之前都有一个查找步骤,以确定应用程序的哪个实例负责给定的数据。 正如我们所说,数据分区通常在数据存储级别应用和处理,因为其主要目的是克服与处理大型整体数据集(有限的磁盘空间、内存和网络容量)相关的问题。 仅对于复杂的分布式架构或非常特殊的用例,才值得考虑在应用程序级别应用它,例如,当构建依赖于数据持久性的自定义解决方案的应用程序时,当使用不支持分区的数据库时,或者当 构建 Google 规模的应用程序。 考虑到其复杂性,只有在充分利用缩放立方体的 X 和 Y 轴之后,才应考虑沿 Z 轴缩放应用程序。

在以下部分中,我们将重点关注用于扩展 Node.js 应用程序的两种最常见和最有效的技术,即按功能/服务进行克隆和分解。